Deno hat sich seit der ersten Version von Ryan Dahl stark gewandelt. Mit Deno 2.0 ist die Runtime produktionsreif: npm-Pakete laufen nativ, TypeScript ist eingebaut, und das Security-Modell macht Exploits strukturell schwieriger. Claude Code versteht Deno-Projekte vollständig — von deno.json über Import-Maps bis hin zu Deno.openKv().
Dieser Guide zeigt, wie du Deno 2.0 mit Claude Code produktiv einsetzt: von den ersten Schritten über Deno KV als eingebettete Datenbank, das Fresh Framework für SSR-Apps, bis hin zur vollständigen Migration von bestehenden Node.js-Projekten.
1. Deno 2.0 Basics
Deno ist eine sichere JavaScript- und TypeScript-Runtime, die auf V8 und Rust basiert. Im Gegensatz zu Node.js läuft Code standardmäßig ohne Dateisystem- oder Netzwerkzugriff — Berechtigungen müssen explizit gesetzt werden.
Installation und erste Schritte
# Deno installieren (Linux/macOS)
curl -fsSL https://deno.land/install.sh | sh
# Version prüfen
deno --version
# deno 2.1.4 (release, x86_64-unknown-linux-gnu)
# TypeScript direkt ausführen — kein Kompilieren nötig
deno run main.ts
# Mit Netzwerkzugriff
deno run --allow-net server.ts
# Alle Permissions (Entwicklung)
deno run --allow-all dev.ts
Wichtige CLI-Befehle
⚡ Deno CLI Toolchain
deno run— Skript ausführen (mit Permission-Flags)deno task— Tasks aus deno.json ausführen (wie npm run)deno fmt— Code formatieren (eingebaut, kein Prettier nötig)deno lint— Linting (eingebaut, kein ESLint nötig)deno test— Tests ausführen (eingebaut, kein Jest nötig)deno compile— Standalone-Binary erstellendeno info— Dependency-Graph anzeigendeno doc— Dokumentation generieren
deno.json — Das Konfigurationsformat
// deno.json
{
"name": "my-app",
"version": "1.0.0",
"tasks": {
"dev": "deno run --allow-net --allow-read --watch main.ts",
"start": "deno run --allow-net --allow-read main.ts",
"test": "deno test --allow-net",
"fmt": "deno fmt",
"lint": "deno lint"
},
"imports": {
"@std/http": "jsr:@std/http@^1.0.0",
"@std/path": "jsr:@std/path@^1.0.0",
"zod": "npm:zod@^3.22.0"
},
"compilerOptions": {
"strict": true,
"lib": ["deno.window"]
}
}
Permission-System — Sicherheit by Default
// Deno blockiert unerlaubte Operationen zur Laufzeit
// ❌ Fehler ohne --allow-net:
const res = await fetch("https://api.example.com");
// PermissionDenied: Requires net access to "api.example.com"
// Granulare Permissions:
deno run --allow-net=api.example.com,cdn.example.com main.ts
deno run --allow-read=/tmp,/var/data main.ts
deno run --allow-write=/tmp main.ts
deno run --allow-env=DATABASE_URL,PORT main.ts
deno run --allow-run=git,curl main.ts
--allow-all in Production.
TypeScript nativ — kein Build-Step
// types.ts
interface User {
id: string;
name: string;
email: string;
createdAt: Date;
}
// main.ts — direkt ausführbar, keine tsconfig.json nötig
import type { User } from "./types.ts";
async function getUser(id: string): Promise<User> {
const res = await fetch(`/api/users/${id}`);
return res.json();
}
// deno run --allow-net main.ts
2. npm-Kompatibilität
Deno 2.0 kann nahezu alle npm-Pakete direkt verwenden — ohne separaten Install-Schritt. Der npm:-Specifier lädt Pakete on-demand und cached sie lokal.
npm: Specifier
// Direkte npm-Imports ohne Installation
import express from "npm:express@4";
import { z } from "npm:zod@^3.22";
import _ from "npm:lodash";
import dayjs from "npm:dayjs";
import { marked } from "npm:marked";
// Oder via JSR (Deno's eigene Registry)
import { serve } from "jsr:@std/http@^1.0";
import { join } from "jsr:@std/path@^1.0";
package.json Support
Deno 2.0 erkennt package.json automatisch. Das ermöglicht schrittweise Migration ohne Breaking Changes:
// package.json wird automatisch erkannt
{
"dependencies": {
"express": "^4.18.0",
"zod": "^3.22.0"
}
}
// Deno nutzt node_modules falls vorhanden
// oder löst direkt via npm-Registry auf
deno run --allow-net --allow-read main.ts
# Oder explizit mit node_modules:
deno install # erstellt node_modules
deno run main.ts
Node Built-ins Kompatibilität
// Node.js built-ins funktionieren mit node: prefix
import { readFileSync, writeFileSync } from "node:fs";
import { join, dirname } from "node:path";
import { createServer } from "node:http";
import crypto from "node:crypto";
import { EventEmitter } from "node:events";
import { Buffer } from "node:buffer";
// Globale Node-Variablen sind verfügbar
console.log(process.version); // Node.js compat
console.log(process.env.PORT);
npm:packagename@version-URLs überall empfiehlt sich der imports-Abschnitt in deno.json als zentrale Dependency-Liste. Claude Code pflegt diese Datei automatisch mit.
| Ökosystem | Deno Support | Specifier |
|---|---|---|
| npm packages | ✓ nativ | npm:package@version |
| JSR (Deno Registry) | ✓ nativ | jsr:@scope/package |
| Node built-ins | ✓ nativ | node:fs, node:path |
| URL imports | ✓ nativ | https://deno.land/x/... |
| CommonJS (require) | ⚠ teilweise | via npm: wrapper |
| Native Addons (.node) | ✗ nicht unterstützt | — |
3. Deno KV
Deno KV ist eine eingebettete Key-Value-Datenbank, die direkt in die Deno-Runtime integriert ist. Sie basiert auf SQLite (lokal) und FoundationDB (Deno Deploy). Kein Setup, keine externe Datenbank — einfach Deno.openKv() aufrufen.
Grundlegende Operationen
// Deno KV öffnen (eingebettet, kein Server nötig)
const kv = await Deno.openKv();
// SET: Wert speichern
await kv.set(["users", "user-1"], {
name: "Alice",
email: "alice@example.com",
createdAt: new Date().toISOString()
});
// GET: Wert abrufen
const result = await kv.get(["users", "user-1"]);
console.log(result.value); // { name: "Alice", ... }
// DELETE: Wert löschen
await kv.delete(["users", "user-1"]);
// LIST: Alle User auflisten
const entries = kv.list({ prefix: ["users"] });
for await (const entry of entries) {
console.log(entry.key, entry.value);
}
Atomic Transactions
// Atomare Transaktionen — entweder alles oder nichts
const userKey = ["users", "user-1"];
const counterKey = ["counters", "users"];
const currentUser = await kv.get(userKey);
const res = await kv
.atomic()
.check({ key: userKey, versionstamp: currentUser.versionstamp })
.set(userKey, { ...currentUser.value, name: "Bob" })
.sum(counterKey, 1n) // BigInt-Increment
.commit();
if (!res.ok) {
console.log("Conflict! Retry needed.");
}
KV als Session-Store
// Praktisches Beispiel: Session Management
import { serve } from "jsr:@std/http";
const kv = await Deno.openKv();
async function createSession(userId: string): Promise<string> {
const sessionId = crypto.randomUUID();
await kv.set(
["sessions", sessionId],
{ userId, createdAt: Date.now() },
{ expireIn: 86_400_000 } // TTL: 24h in ms
);
return sessionId;
}
async function getSession(sessionId: string) {
const res = await kv.get(["sessions", sessionId]);
return res.value; // null wenn abgelaufen oder nicht vorhanden
}
kv.list(), Cursor-basiertem Blättern und Atomic-Updates für Race-Condition-Sicherheit.
4. Fresh Framework
Fresh ist das offizielle Web-Framework für Deno: SSR-first, keine Build-Pipeline, Islands Architecture für selektive Client-Hydration. Claude Code generiert komplette Fresh-Apps mit typsicheren Routes und Preact-Komponenten.
Projekt erstellen
# Fresh-Projekt scaffolden
deno run -A -r https://fresh.deno.dev my-app
cd my-app
# Entwicklungsserver starten
deno task start
# → http://localhost:8000
Projektstruktur
my-app/
├── routes/ # File-based routing
│ ├── index.tsx # → /
│ ├── blog/
│ │ ├── index.tsx # → /blog
│ │ └── [slug].tsx# → /blog/:slug
│ └── api/
│ └── users.ts # → /api/users (REST API)
├── islands/ # Client-side Preact Komponenten
│ ├── Counter.tsx
│ └── SearchBar.tsx
├── components/ # Shared SSR Komponenten
│ └── Layout.tsx
├── static/ # Statische Assets
└── deno.json
Route mit API Handler
// routes/blog/[slug].tsx
import { Handlers, PageProps } from "$fresh/server.ts";
interface Post {
slug: string;
title: string;
content: string;
}
export const handler: Handlers<Post> = {
async GET(req, ctx) {
const { slug } = ctx.params;
const kv = await Deno.openKv();
const post = await kv.get(["posts", slug]);
if (!post.value) {
return ctx.renderNotFound();
}
return ctx.render(post.value as Post);
}
};
export default function BlogPost({ data }: PageProps<Post>) {
return (
<article>
<h1>{data.title}</h1>
<div dangerouslySetInnerHTML={{ __html: data.content }} />
</article>
);
}
Islands Architecture
// islands/Counter.tsx — wird CLIENT-SEITIG hydriert
import { useSignal } from "@preact/signals";
export default function Counter() {
const count = useSignal(0);
return (
<div class="counter">
<button onClick={() => count.value--}>-</button>
<span>{count}</span>
<button onClick={() => count.value++}>+</button>
</div>
);
}
// routes/index.tsx — Island einbinden
import Counter from "../islands/Counter.tsx";
export default function Home() {
return (
<main>
<h1>Willkommen</h1>
{/* Nur Counter wird client-seitig geladen */}
<Counter />
</main>
);
}
5. Deno Deploy
Deno Deploy ist die Edge-Cloud-Plattform von Deno Land, optimiert für Fresh und Deno-Services. Code läuft in über 35 Regionen weltweit, Deno KV ist automatisch integriert — kein Infrastruktur-Setup nötig.
Deployment mit deployctl
# deployctl installieren
deno install -gArf jsr:@deno/deployctl
# Direkt deployen
deployctl deploy --project=my-app main.ts
# Mit Environment Variables
deployctl deploy \
--project=my-app \
--env=DATABASE_URL=postgres://... \
--env=API_KEY=secret \
main.ts
# Status prüfen
deployctl deployments list --project=my-app
GitHub Actions CI/CD
# .github/workflows/deploy.yml
name: Deploy to Deno Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
- name: Deploy to Deno Deploy
uses: denoland/deployctl@v1
with:
project: my-app
entrypoint: main.ts
root: .
Edge-Funktionen mit KV
// main.ts — Edge-ready API mit Deno KV
import { serve } from "jsr:@std/http";
const kv = await Deno.openKv();
// Auf Deno Deploy: nutzt automatisch FoundationDB
serve(async (req) => {
const url = new URL(req.url);
// Rate limiting via KV
const ip = req.headers.get("x-forwarded-for") || "unknown";
const rateKey = ["rate", ip, Math.floor(Date.now() / 60000)];
const rate = await kv.get<number>(rateKey);
if ((rate.value || 0) > 100) {
return new Response("Rate limit exceeded", { status: 429 });
}
await kv.atomic()
.sum(rateKey, 1n)
.commit();
return new Response("OK");
});
Deploy Deno Deploy Features
- 35+ Edge-Regionen weltweit, automatisches Routing
- Deno KV mit FoundationDB-Backend (repliziert, konsistent)
- Custom Domains + automatisches TLS
- GitHub-Integration (Preview Deployments per Branch)
- Zero Cold Starts (V8 Isolates, kein Container-Overhead)
- Kostenloser Tier: 1M Requests/Monat, 512 MB KV-Storage
6. Migration von Node.js
Deno 2.0 ist auf maximale Node.js-Kompatibilität ausgelegt. Viele Projekte laufen mit minimalen Änderungen. Claude Code analysiert ein Node.js-Projekt und erstellt einen strukturierten Migrationsplan.
Typische Unterschiede im Überblick
| Aspekt | Node.js | Deno |
|---|---|---|
| TypeScript | ts-node / tsc nötig | nativ, kein Setup |
| Packages | npm install + node_modules |
npm: Specifier / JSR |
| Konfiguration | package.json + tsconfig | deno.json (alles in einem) |
| Linting/Formatting | ESLint + Prettier | deno lint + deno fmt |
| Tests | Jest / Vitest | deno test (eingebaut) |
| Permissions | vollen Zugriff | explizit, granular |
| __dirname / __filename | nativ | import.meta.dirname |
Häufige Migration-Patterns
// ❌ Node.js: CommonJS require
const express = require("express");
const path = require("path");
// ✅ Deno: ESM imports
import express from "npm:express";
import { join } from "node:path";
// ❌ Node.js: __dirname
const dir = __dirname;
// ✅ Deno: import.meta
const dir = import.meta.dirname;
// ❌ Node.js: process.env ohne Permission
const port = process.env.PORT;
// ✅ Deno: mit --allow-env Flag
// deno run --allow-env=PORT main.ts
const port = Deno.env.get("PORT");
Deno Compat Layer
// deno.json — Node.js Compat aktivieren
{
"nodeModulesDir": "auto",
"unstable": ["bare-node-builtins"]
}
# Für maximale Kompatibilität:
deno run --unstable-bare-node-builtins \
--unstable-byonm \
main.ts
Express App migrieren
// Node.js Express → Deno (praktisch identisch!)
import express from "npm:express@4";
import cors from "npm:cors";
import { z } from "npm:zod";
const app = express();
app.use(express.json());
app.use(cors());
const kv = await Deno.openKv(); // Deno KV statt Redis/MongoDB
app.get("/users/:id", async (req, res) => {
const user = await kv.get(["users", req.params.id]);
if (!user.value) {
return res.status(404).json({ error: "Not found" });
}
res.json(user.value);
});
app.listen(3000, () => console.log("Server läuft"));
// deno run --allow-net --allow-read main.ts
.node-Dateien (wie bcrypt, sharp in manchen Versionen) laufen nicht in Deno. Verwende Pure-JS-Alternativen oder die Deno-eigenen APIs wie crypto.subtle statt bcrypt.
Migrationsstrategie mit Claude Code
Schrittweise Migration (empfohlen)
- Schritt 1:
deno check main.ts— TypeScript-Fehler identifizieren - Schritt 2: CommonJS require → ESM import (Claude Code erledigt das automatisch)
- Schritt 3: node_modules durch npm:/jsr: Specifier ersetzen
- Schritt 4: __dirname/__filename auf import.meta umstellen
- Schritt 5: deno.json mit Tasks, Imports und Permissions erstellen
- Schritt 6: Tests mit
deno testausführen (Jest-Syntax wird erkannt) - Schritt 7: Deployment via deployctl oder Dockerfile (Deno-Base-Image)
Deno-Projekte mit Claude Code aufbauen
Claude Code versteht Deno vollständig — von deno.json über Deno KV bis zu Fresh Routes und Deno Deploy CI/CD. Starte jetzt kostenlos und baue sichere, moderne JS/TS-Apps.
Kostenlos testen →