Bun Basics
Bun runtime fundamentals including speed optimizations, built-in APIs, and package management
You are an expert in Bun runtime fundamentals for building high-performance JavaScript and TypeScript applications. ## Key Points - **Use `Bun.file()` over `fs.readFile`** — it is lazy and significantly faster. Access `.text()`, `.json()`, `.arrayBuffer()` only when you need the data. - **Prefer `bun:sqlite`** for local data persistence — it is embedded, requires no external process, and is extremely fast. - **Use `Bun.serve`** instead of Express for new projects — it is simpler and handles significantly more requests per second. - **Run scripts with `bun run`** — it starts faster than `node` and transpiles TypeScript automatically. - **Use workspace support** in `package.json` for monorepos — Bun respects the same workspace protocol as npm/yarn. - **Commit `bun.lockb`** to version control for deterministic installs. - **Native Node addons (`.node` files)** — Bun supports many but not all N-API addons. C++ addons using raw V8 APIs will not work. - **`bun.lockb` is binary** — you cannot review it in PRs like `package-lock.json`. Use `bun install --yarn` during migration if your team needs a readable lockfile. - **Global installs behave differently** — `bun install -g` installs to `~/.bun/bin`. Ensure it is on your `PATH`. - **Hot module replacement** — `bun --hot` re-executes the module but preserves global state, which can cause subtle bugs if your module has side effects. - **Memory usage patterns differ from Node** — JSC has a different garbage collector. Monitor memory in production and adjust if your workload relies on V8-specific GC behavior. ## Quick Example ```bash bun run app.ts # Just works bun run app.tsx # JSX works too ```
skilldb get deno-bun-skills/Bun BasicsFull skill: 238 linesBun Basics — Modern JS Runtimes
You are an expert in Bun runtime fundamentals for building high-performance JavaScript and TypeScript applications.
Overview
Bun is an all-in-one JavaScript runtime built on the JavaScriptCore engine (from WebKit) rather than V8. It includes a package manager, bundler, test runner, and transpiler in a single binary. Bun is designed as a drop-in replacement for Node.js with dramatically faster startup, install times, and I/O throughput. It runs TypeScript and JSX natively without configuration.
Core Concepts
Runtime Architecture
Bun uses JavaScriptCore (JSC) instead of V8, Zig for its internals instead of C++, and implements hot paths with hand-tuned native code. This yields significant performance gains for startup time, HTTP serving, file I/O, and package installation.
Package Manager
Bun's package manager is compatible with package.json and node_modules but installs packages significantly faster than npm or yarn.
bun init # Create a new project
bun install # Install all dependencies
bun add express # Add a dependency
bun add -d typescript # Add a dev dependency
bun remove lodash # Remove a dependency
bun update # Update all dependencies
The lockfile is bun.lockb (binary format for speed). Use bun install --yarn to generate a yarn.lock if needed for compatibility.
TypeScript and JSX Support
Bun transpiles TypeScript and JSX on the fly. No tsc, no Babel, no configuration:
bun run app.ts # Just works
bun run app.tsx # JSX works too
Built-in APIs
Bun extends the Web Standard APIs with high-performance native implementations:
// Bun.serve — HTTP server
// Bun.file — fast file I/O
// Bun.write — fast file writing
// Bun.spawn / Bun.spawnSync — subprocesses
// Bun.password — password hashing (argon2, bcrypt)
// Bun.sql — built-in PostgreSQL client
// Bun.redis — built-in Redis client
Implementation Patterns
HTTP Server
Bun.serve({
port: 3000,
fetch(req) {
const url = new URL(req.url);
if (url.pathname === "/api/health") {
return Response.json({ status: "ok" });
}
if (url.pathname === "/api/data" && req.method === "POST") {
const body = await req.json();
return Response.json({ received: body }, { status: 201 });
}
return new Response("Not Found", { status: 404 });
},
error(err) {
return new Response(`Internal Error: ${err.message}`, { status: 500 });
},
});
console.log("Server running on http://localhost:3000");
File I/O
// Reading files — returns a BunFile (lazy, no immediate read)
const file = Bun.file("./data.json");
const text = await file.text(); // as string
const json = await file.json(); // parsed JSON
const bytes = await file.arrayBuffer(); // as ArrayBuffer
console.log(file.size, file.type); // metadata without reading
// Writing files
await Bun.write("./output.txt", "Hello, Bun!");
await Bun.write("./copy.json", Bun.file("./source.json")); // copy file
await Bun.write("./data.bin", new Uint8Array([1, 2, 3]));
// Streaming large files
const writer = Bun.file("./large-output.csv").writer();
writer.write("id,name\n");
for (const row of rows) {
writer.write(`${row.id},${row.name}\n`);
}
writer.flush();
SQLite (Built-in)
import { Database } from "bun:sqlite";
const db = new Database("app.db");
// Create table
db.run(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL
)
`);
// Prepared statements (recommended for performance)
const insert = db.prepare("INSERT INTO users (name, email) VALUES (?, ?)");
insert.run("Alice", "alice@example.com");
// Query
const getUser = db.prepare("SELECT * FROM users WHERE email = ?");
const user = getUser.get("alice@example.com");
// Transactions
const insertMany = db.transaction((users) => {
for (const u of users) {
insert.run(u.name, u.email);
}
});
insertMany([
{ name: "Bob", email: "bob@example.com" },
{ name: "Carol", email: "carol@example.com" },
]);
Password Hashing
const hash = await Bun.password.hash("my-password", {
algorithm: "argon2id",
memoryCost: 65536,
timeCost: 3,
});
const isValid = await Bun.password.verify("my-password", hash);
Subprocesses
// Simple execution
const result = Bun.spawnSync(["ls", "-la"]);
console.log(result.stdout.toString());
// Async with streaming
const proc = Bun.spawn(["ffmpeg", "-i", "input.mp4", "output.webm"], {
stdout: "pipe",
stderr: "pipe",
});
const output = await new Response(proc.stdout).text();
await proc.exited; // wait for completion
console.log("Exit code:", proc.exitCode);
WebSocket Server
Bun.serve({
port: 3000,
fetch(req, server) {
if (server.upgrade(req)) return;
return new Response("Upgrade failed", { status: 500 });
},
websocket: {
open(ws) {
ws.subscribe("chat");
ws.publish("chat", JSON.stringify({ type: "join" }));
},
message(ws, message) {
ws.publish("chat", message);
},
close(ws) {
ws.unsubscribe("chat");
},
},
});
Core Philosophy
Bun is built on the premise that developer tooling should be fast enough to be invisible. By combining a runtime, package manager, bundler, test runner, and transpiler into a single binary, Bun eliminates the configuration overhead and startup latency that plague traditional Node.js toolchains. You run bun run app.ts and it just works, with TypeScript, JSX, and module resolution handled automatically.
The performance story is not just marketing: Bun uses JavaScriptCore (the WebKit engine) instead of V8, implements hot paths in Zig, and optimizes I/O at the system call level. These choices produce measurably faster HTTP serving, file operations, and package installations. But performance is a means to an end, not the end itself. The real value is that Bun reduces the friction between writing code and seeing results, enabling tighter development loops.
Bun's compatibility strategy is pragmatic rather than purist. It implements Node.js APIs directly so that existing package.json projects, npm packages, and node_modules directories work without modification. At the same time, it provides Bun-native APIs (Bun.serve, Bun.file, Bun.password, bun:sqlite) that are simpler and faster than their Node equivalents. The migration path is: swap the runtime first, adopt Bun-native APIs gradually.
Anti-Patterns
-
Using
fs.readFilewhenBun.file()is available.Bun.file()is lazy (no read until you call.text()or.json()), significantly faster, and provides metadata without reading the file. Defaulting to the NodefsAPI when writing new code misses performance gains. -
Installing Express for new projects instead of using
Bun.serve. Express works on Bun, butBun.serveis simpler (one function, Web Fetch API) and handles significantly more requests per second. Use Express only when you need its middleware ecosystem. -
Assuming all Node.js APIs are fully implemented. Bun covers the most common Node built-ins but niche APIs may be missing or behave differently. Check the compatibility table for your specific dependency before deploying to production.
-
Using
bun --hotwithout understanding its semantics. Hot reloading re-executes the module but preserves global state, which can cause subtle bugs if your module has initialization side effects. Clear global state explicitly or use--watch(full restart) for modules with side effects. -
Relying on V8-specific behavior in production. JavaScriptCore has a different garbage collector, different JIT characteristics, and different error formatting than V8. Profile and test on Bun itself rather than assuming Node benchmarks transfer directly.
Best Practices
- Use
Bun.file()overfs.readFile— it is lazy and significantly faster. Access.text(),.json(),.arrayBuffer()only when you need the data. - Prefer
bun:sqlitefor local data persistence — it is embedded, requires no external process, and is extremely fast. - Use
Bun.serveinstead of Express for new projects — it is simpler and handles significantly more requests per second. - Run scripts with
bun run— it starts faster thannodeand transpiles TypeScript automatically. - Use workspace support in
package.jsonfor monorepos — Bun respects the same workspace protocol as npm/yarn. - Commit
bun.lockbto version control for deterministic installs.
Common Pitfalls
- Not all Node.js APIs are implemented — Bun covers the most common ones (
fs,path,http,crypto,child_process) but niche APIs may be missing or behave slightly differently. Check the compatibility table. - Native Node addons (
.nodefiles) — Bun supports many but not all N-API addons. C++ addons using raw V8 APIs will not work. bun.lockbis binary — you cannot review it in PRs likepackage-lock.json. Usebun install --yarnduring migration if your team needs a readable lockfile.- Global installs behave differently —
bun install -ginstalls to~/.bun/bin. Ensure it is on yourPATH. - Hot module replacement —
bun --hotre-executes the module but preserves global state, which can cause subtle bugs if your module has side effects. - Memory usage patterns differ from Node — JSC has a different garbage collector. Monitor memory in production and adjust if your workload relies on V8-specific GC behavior.
Install this skill directly: skilldb add deno-bun-skills
Related Skills
Bun Bundler
Using Bun as a bundler for frontend assets and as a fast test runner
Compatibility
Node.js compatibility layers in Deno and Bun for running existing npm packages and Node APIs
Deno Basics
Deno runtime fundamentals including permissions, module system, and built-in tooling
Deno Deploy
Deno Deploy edge functions for globally distributed serverless applications
Elysia Bun
Elysia web framework on Bun for type-safe, high-performance HTTP APIs
Fresh Framework
Fresh full-stack web framework for Deno with islands architecture and zero client JS by default