Bun ist mehr als eine Node.js-Alternative. Die Runtime kombiniert Zig als Systemsprache mit Apples JavaScriptCore-Engine und erreicht damit Performance-Werte, die klassische JavaScript-Runtimes nicht bieten können. Ein Blick auf die Architektur zeigt, warum.
Zig und JavaScriptCore
Während Node.js auf V8 (Chrome) setzt, verwendet Bun JavaScriptCore aus Safari. Der entscheidende Unterschied liegt aber tiefer: Buns Event Loop, Task Scheduler und native I/O-Operationen sind direkt in Zig implementiert.
Was das bringt:
- Bis zu 4x schnellere Startzeiten
- Niedrigere Latenz bei I/O-Operationen
- Optimierte Module-Resolution und Caching
Zig ermöglicht direkten Speicherzugriff ohne Garbage-Collection-Overhead. Das macht sich besonders bei häufigen, kleinen Operationen bemerkbar – genau das, was Server-Anwendungen ständig tun.
Plugin-System
Buns Bundler ist über Plugins erweiterbar. Entwickler können in den Build-Prozess eingreifen und eigene Loader definieren:
import type { BunPlugin } from "bun";
const textPlugin: BunPlugin = {
name: "text-loader",
setup(build) {
build.onLoad({ filter: /\.txt$/ }, async (args) => {
const text = await Bun.file(args.path).text();
return {
contents: `export default ${JSON.stringify(text)}`,
loader: "js",
};
});
},
};
Mit build.onLoad lassen sich beliebige Dateitypen zu JavaScript-Modulen transformieren. Das ermöglicht:
- Custom Loader für proprietäre Formate
- Preprocessing von Assets
- Lazy Loading für Fullstack-Anwendungen
Tree-Shaking und Minification sind dabei automatisch integriert.
Tree-Shaking im Detail
Buns Bundler analysiert Code statisch und entfernt ungenutzte Exports durch Dead-Code-Elimination – standardmäßig aktiviert.
Bei ESM-Modulen:
// foo.js
export const foo = () => "foo";
export const bar = () => "bar";
// main.js
import { foo } from "./foo.js";
// bar wird automatisch entfernt
Der Bundler erkennt, welche Symbole tatsächlich referenziert werden und schließt nur diese ein.
Bei CommonJS:
Dynamische Exports wie module.exports = require(...) verhindern statische Analyse. In diesen Fällen bleibt das gesamte exports-Objekt erhalten. Bun konvertiert CommonJS bei Bedarf zu ESM und injiziert minimalen Wrapper-Code.
Build-Konfiguration:
await Bun.build({
entrypoints: ["./src/index.ts"],
outdir: "./dist",
target: "bun",
minify: true,
env: "inline", // Tree-Shaking für Umgebungsvariablen
});
Native SQLite-Integration
Das bun:sqlite-Modul ist direkt in die Runtime integriert – kein npm-Paket, keine nativen Bindings:
import { Database } from "bun:sqlite";
const db = new Database("app.db");
// Prepared Statements
const query = db.prepare("SELECT * FROM users WHERE id = ?");
const user = query.get(1);
// Transactions
db.transaction(() => {
db.run("INSERT INTO users (name) VALUES (?)", ["Alice"]);
db.run("INSERT INTO users (name) VALUES (?)", ["Bob"]);
})();
Performance-Vorteile:
- 3-6x schneller als better-sqlite3
- Prepared Statements mit Caching
- Named und Positional Parameters
- Automatische Datatype-Conversions (BLOB → Uint8Array)
Für Low-Level-Zugriff gibt es db.fileControl mit direktem Zugang zu SQLite3-APIs. Results lassen sich auf Klassen mappen für type-safe Queries.
Native APIs
Bun bringt APIs mit, die über Standard-JavaScript hinausgehen:
Foreign Function Interface:
import { dlopen, FFIType } from "bun:ffi";
const lib = dlopen("libcrypto.so", {
MD5: {
args: [FFIType.ptr, FFIType.u64, FFIType.ptr],
returns: FFIType.ptr,
},
});
Memory-Mapped Files:
const file = Bun.file("large-data.bin");
const mapped = Bun.mmap(file);
Diese APIs ermöglichen Optimierungen, die mit Node.js nur über native Addons möglich wären.
Edge-Deployment
Für Edge-Szenarien kombiniert Bun gut mit Frameworks wie Hono:
import { Hono } from "hono";
const app = new Hono();
app.get("/", (c) => c.json({ message: "Hello from the edge" }));
export default app;
Buns schnelle Startzeiten und niedriger Memory-Footprint machen es ideal für:
- Cloudflare Workers
- Fly.io
- Serverless-Funktionen
Die Empfehlung: Stateless Logic und minimale Dependencies priorisieren.