Zum Inhalt springen

Deno

Deno is a secure runtime for JavaScript and TypeScript built on V8 and Rust. Unlike Node.js, Deno is secure by default — scripts run in a sandbox and must be granted explicit permissions to access the filesystem, network, and environment.

Installation

# macOS / Linux (shell installer)
curl -fsSL https://deno.land/install.sh | sh

# macOS (Homebrew)
brew install deno

# Windows (PowerShell)
irm https://deno.land/install.ps1 | iex

# Windows (Scoop)
scoop install deno

# Upgrade existing installation
deno upgrade

# Upgrade to specific version
deno upgrade --version 1.45.0

# Check installed version
deno --version

Add Deno to your shell path after installation:

export DENO_INSTALL="$HOME/.deno"
export PATH="$DENO_INSTALL/bin:$PATH"

Configuration

Deno uses deno.json (or deno.jsonc) for project configuration:

{
  "name": "my-app",
  "version": "1.0.0",
  "tasks": {
    "dev": "deno run --watch --allow-net --allow-read src/main.ts",
    "build": "deno compile --allow-net --allow-read src/main.ts",
    "test": "deno test --allow-read tests/",
    "lint": "deno lint",
    "fmt": "deno fmt"
  },
  "imports": {
    "@std/http": "jsr:@std/http@^1.0.0",
    "@std/path": "jsr:@std/path@^1.0.0",
    "hono": "npm:hono@^4.0.0"
  },
  "lint": {
    "include": ["src/", "tests/"],
    "exclude": ["dist/"],
    "rules": {
      "tags": ["recommended"],
      "include": ["ban-untagged-todo"],
      "exclude": ["no-unused-vars"]
    }
  },
  "fmt": {
    "useTabs": false,
    "lineWidth": 100,
    "indentWidth": 2,
    "singleQuote": true,
    "include": ["src/", "tests/"]
  },
  "compilerOptions": {
    "strict": true,
    "lib": ["deno.window"]
  },
  "test": {
    "include": ["tests/**/*.test.ts"]
  }
}

Import Maps

{
  "imports": {
    "std/": "https://deno.land/std@0.224.0/",
    "react": "npm:react@18",
    "lodash": "npm:lodash@4"
  }
}

Core Commands

CommandDescription
deno run file.tsRun a TypeScript/JavaScript file
deno run --watch file.tsRun with file watching (auto-restart)
deno run --allow-all file.tsRun with all permissions enabled
deno task devRun a task defined in deno.json
deno fmtFormat source files
deno fmt --checkCheck formatting without modifying files
deno lintLint source files
deno lint --fixAuto-fix lint issues
deno testRun all test files
deno test --watchRun tests in watch mode
deno test --coverageRun tests with coverage reporting
deno compile file.tsCompile to self-contained executable
deno bundle file.ts out.jsBundle to single JS file (deprecated, use esbuild)
deno installInstall a script as a CLI tool
deno info file.tsShow module dependency tree
deno check file.tsType-check without running
deno doc file.tsGenerate documentation
deno cache file.tsDownload and cache dependencies
deno upgradeUpgrade Deno to latest version
deno replStart interactive REPL

Permissions Model

Permission FlagDescription
--allow-readRead filesystem access
--allow-read=/tmpRead access to specific path
--allow-writeWrite filesystem access
--allow-write=/tmpWrite access to specific path
--allow-netNetwork access (all hosts)
--allow-net=example.comNetwork access to specific host
--allow-envAccess environment variables
--allow-env=HOME,PATHAccess specific env vars
--allow-runSpawn subprocesses
--allow-run=git,npmRun specific executables
--allow-sysAccess system info APIs
--allow-hrtimeHigh-resolution time access
--allow-ffiLoad dynamic libraries
--allow-allAll permissions (use with caution)
--no-promptFail instead of prompting for permissions
--deny-netExplicitly deny network access

Advanced Usage

HTTP Server

import { Hono } from "npm:hono@4";

const app = new Hono();

app.get("/", (c) => c.json({ message: "Hello from Deno!" }));

app.get("/users/:id", (c) => {
  const id = c.req.param("id");
  return c.json({ id, name: "Alice" });
});

Deno.serve({ port: 8000 }, app.fetch);

Deno KV Storage

// Open the default KV store
const kv = await Deno.openKv();

// Set a value
await kv.set(["users", "alice"], { name: "Alice", age: 30 });

// Get a value
const result = await kv.get(["users", "alice"]);
console.log(result.value); // { name: "Alice", age: 30 }

// List entries with prefix
for await (const entry of kv.list({ prefix: ["users"] })) {
  console.log(entry.key, entry.value);
}

// Atomic transactions
await kv.atomic()
  .check({ key: ["counter"], versionstamp: null })
  .set(["counter"], 0)
  .commit();

// Watch for changes
const stream = kv.watch([["counter"]]);
for await (const [entry] of stream) {
  console.log("Counter changed:", entry.value);
}

Compile to Binary

# Compile for current platform
deno compile --allow-net --allow-read src/main.ts -o myapp

# Cross-compile for different targets
deno compile --target x86_64-unknown-linux-gnu src/main.ts
deno compile --target x86_64-pc-windows-msvc src/main.ts
deno compile --target aarch64-apple-darwin src/main.ts

# Include assets in the binary
deno compile --include assets/ src/main.ts

Working with npm Packages

// Direct npm imports (no install needed)
import express from "npm:express@4";
import { z } from "npm:zod@3";

// Using JSR (JavaScript Registry)
import { assertEquals } from "jsr:@std/assert@1";

// Node.js built-ins via node: specifier
import { readFileSync } from "node:fs";
import path from "node:path";

Testing

import { assertEquals, assertThrows } from "jsr:@std/assert";

Deno.test("basic test", () => {
  assertEquals(1 + 1, 2);
});

Deno.test("async test", async () => {
  const response = await fetch("https://api.example.com/data");
  assertEquals(response.status, 200);
});

Deno.test({
  name: "permission test",
  permissions: { net: true },
  async fn() {
    const res = await fetch("https://example.com");
    assertEquals(res.ok, true);
  },
});

// Test coverage
// deno test --coverage=cov_profile
// deno coverage cov_profile --lcov > coverage.lcov

File System Operations

// Read file
const text = await Deno.readTextFile("./data.txt");
const bytes = await Deno.readFile("./image.png");

// Write file
await Deno.writeTextFile("./output.txt", "Hello, World!");

// Watch for changes
const watcher = Deno.watchFs("./src");
for await (const event of watcher) {
  console.log(event.kind, event.paths);
}

// Temp files
const tmpFile = await Deno.makeTempFile({ prefix: "deno_" });

Common Workflows

Starting a New Project

# Initialize project with deno.json
deno init my-project
cd my-project

# Or manually create deno.json
cat > deno.json << 'EOF'
{
  "tasks": {
    "dev": "deno run --watch --allow-net src/main.ts",
    "test": "deno test --allow-read"
  }
}
EOF

# Run development server
deno task dev

Migrating from Node.js

# Install npm packages without a package.json
# Just import directly:
# import lodash from "npm:lodash";

# Or add to deno.json imports section
# Run node-compatible scripts
deno run --allow-all --node-modules-dir npm_script.js

# Compatibility layer
deno run --allow-all --compat legacy_node.js

Setting Up CI (GitHub Actions)

name: CI
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: denoland/setup-deno@v2
        with:
          deno-version: v2.x
      - run: deno fmt --check
      - run: deno lint
      - run: deno test --allow-read --coverage=cov_profile
      - run: deno coverage cov_profile --lcov > coverage.lcov

Deploy to Deno Deploy

# Install deployctl
deno install -gArf jsr:@deno/deployctl

# Deploy from local files
deployctl deploy --project=my-project src/main.ts

# Deploy with env vars
deployctl deploy --project=my-project --env=DATABASE_URL=postgres://... src/main.ts

Tips and Best Practices

Use explicit permissions — Avoid --allow-all in production. Grant only the minimum required permissions and use path-specific grants like --allow-read=./data rather than blanket --allow-read.

Prefer JSR over deno.land/x — The JavaScript Registry (jsr.io) is the modern package registry for Deno and Node. It offers type safety, provenance, and better tooling than the old third-party module registry.

Lock your dependencies — Run deno cache --lock=deno.lock src/main.ts to generate a lockfile. Commit deno.lock to version control. Use --frozen in CI to ensure dependencies don’t drift.

Use deno.json tasks — Define all common commands in the tasks field of deno.json instead of a Makefile or shell scripts. This keeps commands portable and self-documenting.

Type-check separately from running — Use deno check src/main.ts in CI for fast type-checking without executing the program.

Leverage built-in tooling — Deno ships with a formatter (deno fmt), linter (deno lint), test runner (deno test), and doc generator (deno doc) — use them instead of separate npm packages.

Watch mode for developmentdeno run --watch restarts on file changes. deno test --watch re-runs tests. Both are built-in alternatives to nodemon or chokidar.

Vendor dependencies for offline builds — Run deno vendor to download all dependencies into a local vendor/ directory. Useful for air-gapped environments or reproducible builds.

# Vendor all dependencies
deno vendor src/main.ts

# Run using vendored deps (no network needed)
deno run --no-remote --import-map=vendor/import_map.json src/main.ts

Use Deno KV for simple persistence — For apps that need key-value storage without a full database, Deno KV works locally and scales seamlessly on Deno Deploy with zero configuration.