JavaScript-Entwickler kennen das Problem: Node.js, npm, webpack, Jest, ts-node — jedes Tool eine eigene Konfiguration, jedes Ökosystem eigene Eigenheiten. Bun.js löst das mit einem radikalen Ansatz: eine einzige Binary für Runtime, Package Manager, Bundler, Test Runner und sogar SQLite-Datenbankzugriff. Claude Code erkennt Bun-Projekte automatisch und optimiert seine Vorschläge entsprechend.

🥟 Bun auf einen Blick

  • Runtime Ersetzt Node.js mit JavaScriptCore-Engine
  • Package Manager Ersetzt npm/yarn/pnpm — bis zu 25× schneller
  • Bundler Ersetzt webpack/esbuild — nativ in Bun
  • Test Runner Ersetzt Jest/Vitest — kompatible API
  • SQLite Eingebaute Datenbank — kein Treiber nötig

1. Bun als Runtime — bun run, Bun.serve() und Vergleich zu Node.js

Bun basiert auf JavaScriptCore (die Engine hinter Safari/WebKit), nicht auf V8 wie Node.js. Das macht Bun in I/O-intensiven Workloads messbar schneller, besonders beim Starten von Prozessen und beim Verarbeiten von HTTP-Anfragen. TypeScript und JSX werden ohne Transpilierung direkt ausgeführt — kein ts-node, kein Babel nötig.

Direkte Skriptausführung

# Node.js benötigt ts-node oder esbuild
npx ts-node server.ts

# Bun führt TypeScript nativ aus
bun run server.ts

# Oder einfach direkt:
bun server.ts

HTTP-Server mit Bun.serve()

// server.ts — TypeScript direkt, kein Build-Schritt
const server = Bun.serve({
  port: 3000,
  async fetch(req: Request): Promise<Response> {
    const url = new URL(req.url);

    if (url.pathname === "/health") {
      return Response.json({ status: "ok", runtime: "bun" });
    }

    if (url.pathname === "/api/users") {
      const users = db.query("SELECT * FROM users LIMIT 100").all();
      return Response.json(users);
    }

    return new Response("Not Found", { status: 404 });
  },
});

console.log(`Server läuft auf http://localhost:${server.port}`);

Benchmark: Bun vs. Node.js vs. Deno

BenchmarkNode.js 22Deno 2Bun 1.2
Startup-Zeit (cold)42 ms51 ms9 ms
HTTP req/s (hello world)68.00071.000210.000
JSON parse 10 MB145 ms138 ms62 ms
npm install (100 Pakete)12,4 s9,1 s0,6 s
TypeScript kompilieren4,2 s (tsc)3,8 s0,3 s

Hinweis für Claude Code: Wenn in deinem Projekt eine bun.lockb vorhanden ist, erkennt Claude Code das automatisch als Bun-Projekt. Claude schlägt dann bun run statt npm run vor und nutzt die Bun-spezifischen APIs in seinen Code-Generierungen.

JSX und TypeScript nativ

// App.tsx — funktioniert direkt mit bun run App.tsx
import { renderToString } from "react-dom/server";

function App({ name }: { name: string }) {
  return <h1>Hallo, {name}!</h1>;
}

const html = renderToString(<App name="Welt" />);
console.log(html); // <h1>Hallo, Welt!</h1>

2. Package Manager — bun install, bun.lockb und Workspaces

Buns Package Manager ist der schnellste auf dem Markt. Er verwendet ein binäres Lockfile-Format (bun.lockb), globales Caching und parallele Downloads. Die Kompatibilität zu npm ist vollständig — alle npm-Pakete funktionieren ohne Änderungen.

Grundlegende Befehle

# Alle Abhängigkeiten installieren
bun install

# Paket hinzufügen (Produktion)
bun add express zod

# Dev-Dependency hinzufügen
bun add -d @types/express vitest

# Paket entfernen
bun remove lodash

# Paket aktualisieren
bun update react

# Globale Installation
bun add -g typescript

package.json — Scripts mit bun

{
  "name": "my-app",
  "scripts": {
    "dev": "bun run --hot src/index.ts",
    "build": "bun build src/index.ts --outdir dist",
    "test": "bun test",
    "start": "bun run dist/index.js"
  },
  "dependencies": {
    "hono": "^4.0.0",
    "zod": "^3.22.0"
  }
}

Workspaces (Monorepo)

# Root package.json
{
  "workspaces": ["packages/*", "apps/*"],
  "scripts": {
    "dev": "bun run --filter '*' dev",
    "build": "bun run --filter '*' build"
  }
}

# In einem Workspace-Paket installieren
bun add react --cwd apps/web

# Alle Workspaces listen
bun pm ls

npm-Registries und private Pakete

# .bunfig.toml — Bun-Konfiguration
[install]
registry = "https://registry.npmjs.org"

[install.scopes]
"@meinunternehmen" = {
  registry = "https://npm.meinunternehmen.de",
  token = "$NPM_TOKEN"
}

Lockfile-Tipp: Das binäre bun.lockb ist kleiner und schneller zu parsen als package-lock.json. Claude Code liest es automatisch und kann daraus Abhängigkeitsgraphen ableiten. Bei Konflikten mit Tools die JSON erwarten: bun install --frozen-lockfile für CI.

3. Bun als Bundler — bun build, Minifizierung und Targets

Bun enthält einen vollständigen Bundler ohne externe Abhängigkeiten. Er ist darauf ausgelegt, webpack und esbuild in den meisten Anwendungsfällen zu ersetzen — mit nativ schneller Performance durch JavaScriptCore-Optimierungen.

Grundlegender Build

# Single-Entry-Point Build
bun build src/index.ts --outdir dist

# Minifiziert + Source Maps
bun build src/index.ts --outdir dist --minify --sourcemap=external

# Für Browser (ESM)
bun build src/app.tsx --outdir dist/browser --target browser

# Für Node.js
bun build src/server.ts --outdir dist/server --target node

# Als Bibliothek bauen
bun build src/lib.ts --outdir dist --format esm

Programmgesteuert via API

const result = await Bun.build({
  entrypoints: ["./src/index.ts"],
  outdir: "./dist",
  target: "browser",
  minify: {
    whitespace: true,
    identifiers: true,
    syntax: true,
  },
  sourcemap: "external",
  splitting: true,   // Code-Splitting für Lazy Loading
  plugins: [
    bunPlugin()       // Eigene Bun-Plugins
  ],
  define: {
    "process.env.NODE_ENV": '"production"',
    "__VERSION__": '"1.0.0"',
  },
});

if (!result.success) {
  console.error("Build fehlgeschlagen:", result.logs);
  process.exit(1);
}

result.outputs.forEach(out => {
  console.log(`${out.path}: ${out.size} Bytes`);
});

Eigene Plugins schreiben

import type { BunPlugin } from "bun";

const yamlPlugin: BunPlugin = {
  name: "yaml-loader",
  setup(build) {
    build.onLoad({ filter: /\.yaml$/ }, async ({ path }) => {
      const text = await Bun.file(path).text();
      const data = parseYaml(text); // eigene Parse-Funktion
      return {
        contents: `export default ${JSON.stringify(data)}`,
        loader: "js",
      };
    });
  },
};

await Bun.build({
  entrypoints: ["./src/index.ts"],
  plugins: [yamlPlugin],
});

4. Bun Test Runner — bun:test, Mocking und Coverage

bun:test ist ein Jest-kompatibler Test Runner mit identischer API. Bestehende Jest-Tests laufen ohne Änderungen. Die Performance übertrifft Jest und Vitest deutlich, weil keine Transpilierung nötig ist.

Tests schreiben

import { describe, test, expect, beforeEach, afterAll } from "bun:test";

describe("UserService", () => {
  let service: UserService;

  beforeEach(() => {
    service = new UserService(":memory:");
  });

  test("erstellt einen neuen User", () => {
    const user = service.create({ name: "Alice", email: "alice@example.com" });
    expect(user.id).toBeGreaterThan(0);
    expect(user.name).toBe("Alice");
  });

  test("wirft bei doppelter E-Mail", () => {
    service.create({ name: "Alice", email: "a@a.de" });
    expect(() => {
      service.create({ name: "Bob", email: "a@a.de" });
    }).toThrow("E-Mail bereits vergeben");
  });

  test("findet User nach ID", async () => {
    const created = service.create({ name: "Carol", email: "c@c.de" });
    const found = service.findById(created.id);
    expect(found).toEqual(expect.objectContaining({ name: "Carol" }));
  });
});

Mocking mit bun:test

import { mock, spyOn } from "bun:test";

// Funktion mocken
const fetchUser = mock(async (id: number) => ({
  id,
  name: "Mock User",
  email: "mock@example.com",
}));

test("ruft fetchUser einmal auf", async () => {
  const user = await fetchUser(42);
  expect(fetchUser).toHaveBeenCalledTimes(1);
  expect(fetchUser).toHaveBeenCalledWith(42);
  expect(user.name).toBe("Mock User");
});

// Modul-Mock
const consoleSpy = spyOn(console, "log");
test("loggt die richtige Nachricht", () => {
  greet("Welt");
  expect(consoleSpy).toHaveBeenCalledWith("Hallo, Welt!");
});

Tests ausführen und Coverage

# Alle Tests ausführen
bun test

# Mit Coverage
bun test --coverage

# Nur bestimmte Dateien
bun test src/users.test.ts

# Watch-Mode
bun test --watch

# Timeout setzen (ms)
bun test --timeout 5000

Performance-Vergleich

Test-Runner500 Tests (cold)500 Tests (warm)Watch-Restart
Jest 2914,2 s8,1 s2,8 s
Vitest 26,4 s3,2 s0,9 s
bun:test1,1 s0,4 s0,15 s

5. Bun SQLite — eingebaute Datenbank ohne Treiber

bun:sqlite ist direkt in die Bun-Binary integriert. Kein npm install better-sqlite3, keine nativen Addons, keine Kompilierung. Die API ist identisch zu better-sqlite3 — Code lässt sich direkt portieren.

Grundlegende Verwendung

import { Database } from "bun:sqlite";

// Datenbank öffnen (oder erstellen)
const db = new Database("meine-app.sqlite");

// WAL-Mode aktivieren (empfohlen für Performance)
db.exec("PRAGMA journal_mode = WAL;");
db.exec("PRAGMA foreign_keys = ON;");

// Tabelle erstellen
db.exec(`
  CREATE TABLE IF NOT EXISTS users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    email TEXT UNIQUE NOT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
  );
`);

Prepared Statements und Abfragen

// Prepared Statement — einmal vorbereiten, oft nutzen
const insertUser = db.prepare(
  "INSERT INTO users (name, email) VALUES ($name, $email)"
);

const findById = db.prepare(
  "SELECT * FROM users WHERE id = $id"
);

const findAll = db.prepare(
  "SELECT * FROM users ORDER BY created_at DESC LIMIT $limit"
);

// Einfügen
const { lastInsertRowid } = insertUser.run({
  $name: "Alice",
  $email: "alice@example.com",
});

// Einen Datensatz abrufen
const user = findById.get({ $id: lastInsertRowid });
console.log(user); // { id: 1, name: 'Alice', ... }

// Mehrere Datensätze abrufen
const users = findAll.all({ $limit: 50 });
console.log(`${users.length} User gefunden`);

Transaktionen

// Atomare Transaktionen mit db.transaction()
const transferFunds = db.transaction((from: number, to: number, amount: number) => {
  const debit = db.prepare(
    "UPDATE accounts SET balance = balance - $amount WHERE id = $id"
  );
  const credit = db.prepare(
    "UPDATE accounts SET balance = balance + $amount WHERE id = $id"
  );

  debit.run({ $amount: amount, $id: from });
  credit.run({ $amount: amount, $id: to });

  return { success: true, transferred: amount };
});

// Ausführen — automatisches Rollback bei Fehler
const result = transferFunds(1, 2, 500);
console.log(result.transferred); // 500

In-Memory-Datenbank für Tests

import { Database } from "bun:sqlite";
import { describe, test, expect, beforeEach } from "bun:test";

describe("Database Tests", () => {
  let db: Database;

  beforeEach(() => {
    // Jeder Test bekommt eine frische In-Memory-DB
    db = new Database(":memory:");
    db.exec(schema); // Tabellen erstellen
  });

  test("speichert und liest User", () => {
    db.prepare("INSERT INTO users (name) VALUES (?)").run("Test");
    const user = db.prepare("SELECT * FROM users").get();
    expect(user.name).toBe("Test");
  });
});

6. Migration und Kompatibilität — von Node.js zu Bun

Bun hat Node.js-API-Kompatibilität als Ziel, aber es gibt Lücken. Die meisten Express/Fastify/Prisma-Apps laufen ohne Änderungen. Native Node.js-Addons (.node-Dateien) sind die größte Einschränkung.

Was funktioniert reibungslos

// Diese Node.js-APIs funktionieren in Bun identisch:
import fs from "fs";           // ✅ Komplett
import path from "path";       // ✅ Komplett
import http from "http";       // ✅ Komplett
import crypto from "crypto";   // ✅ Komplett
import stream from "stream";   // ✅ Größtenteils
import os from "os";           // ✅ Komplett
import events from "events";   // ✅ Komplett
import buffer from "buffer";   // ✅ Komplett

// process.env funktioniert identisch
const port = process.env.PORT ?? "3000";
const nodeEnv = process.env.NODE_ENV;

Bun-spezifische Umgebungsvariablen

// .env wird automatisch geladen — kein dotenv nötig!
// .env.local, .env.development, .env.production werden ebenfalls geladen

// Zugriff identisch zu Node.js:
const dbUrl = process.env.DATABASE_URL;
const apiKey = process.env.API_KEY;

// Bun.env ist ein Alias für process.env
const secret = Bun.env.JWT_SECRET;

⚠️ Bekannte Einschränkungen: Native Node.js-Addons (.node-Dateien) wie bcrypt, sharp oder canvas werden noch nicht vollständig unterstützt. Nutze Alternativen: bcryptjs (pure JS), sharp über Bun-Shell oder Bun.CryptoHasher für Hashing.

Migration eines Express-Servers

// VORHER: Node.js + Express
import express from "express";
const app = express();
app.get("/", (req, res) => res.json({ ok: true }));
app.listen(3000);

// NACHHER: Bun native (kein npm-Paket nötig)
Bun.serve({
  port: 3000,
  fetch(req) {
    const url = new URL(req.url);
    if (url.pathname === "/") return Response.json({ ok: true });
    return new Response("Not Found", { status: 404 });
  },
});

// ODER: Hono (Express-ähnlich, Bun-optimiert)
import { Hono } from "hono";
const app = new Hono();
app.get("/", (c) => c.json({ ok: true }));
export default app; // Bun erkennt das automatisch

Migrationscheckliste

Schritt-für-Schritt Migration

  • 1. Bun installieren: curl -fsSL https://bun.sh/install | bash
  • 2. bun install statt npm install ausführen — prüfen ob alle Pakete installieren
  • 3. bun run src/index.ts — native Addons identifizieren (Fehler lesen!)
  • 4. Native Addons durch Pure-JS-Alternativen ersetzen
  • 5. Tests mit bun test ausführen — Jest-Tests laufen meist sofort
  • 6. dotenv aus package.json entfernen — Bun lädt .env automatisch
  • 7. ts-node und @types/node können entfernt werden
  • 8. Scripts in package.json von node/ts-node auf bun umstellen

Bun mit Claude Code — Workflow-Optimierungen

# Claude Code erkennt bun.lockb und nutzt automatisch Bun-Befehle
# Nützliche Kombination: Bun + Hot Reload während Claude Code entwickelt

# Terminal 1: Server mit Hot Reload
bun run --hot src/server.ts

# Terminal 2: Tests im Watch-Mode
bun test --watch

# Terminal 3: Claude Code für Code-Generierung
claude

# .claude/settings.json — Claude Code für Bun konfigurieren
{
  "tools": {
    "bash": true
  },
  "env": {
    "BUN_INSTALL": "$HOME/.bun",
    "PATH": "$HOME/.bun/bin:$PATH"
  }
}

Bun.js mit Claude Code ausprobieren

Starte deinen kostenlosen Trial und erlebe, wie Claude Code Bun-Projekte automatisch erkennt, optimierte Snippets generiert und deine Entwicklungsgeschwindigkeit verdoppelt.

Kostenlos Trial starten →