Bun Runtime: Faster Node.js Alternative with Built-in Tooling
Bun is a JavaScript and TypeScript runtime, package manager, bundler, and test runner — all in one. It runs the V8 alternative JavaScriptCore (the engine behind Safari) and is written in Zig, resulting in significant performance improvements over Node.js for many workloads. It also natively runs TypeScript without compilation.
Why Bun
Speed: Bun starts 2-5x faster than Node.js, installs packages ~20-30x faster than npm, and executes many workloads 2-3x faster. For scripts, CLIs, and services with fast startup requirements, the difference is noticeable.
All-in-one: Bun replaces Node.js + npm/yarn/pnpm + ts-node/tsx + Jest + Webpack (in some cases). Fewer tools, faster setup.
TypeScript native: Run .ts files directly without compilation or ts-node:
bun run script.ts
Node.js compatibility: Bun implements most Node.js built-in modules (fs, path, http, crypto, etc.) and require(). Most Node.js packages work without changes.
Installation
curl -fsSL https://bun.sh/install | bash
# or
brew install bun # macOS
Check version:
bun --version
Basic Usage
# Run a script
bun run index.ts
# Run npm scripts
bun run start
bun run build
bun test
# Install packages (lockfile: bun.lockb — much faster than npm)
bun install
bun add express
bun add -D typescript
bun remove lodash
# Run any npm-compatible script
bun x vite # equivalent to npx vite
Bun-Native APIs
Bun provides fast, built-in APIs that outperform equivalent npm packages:
Bun.file (File I/O)
// Read file
const file = Bun.file("./data.json");
const data = await file.json(); // or .text(), .arrayBuffer(), .stream()
// Write file
await Bun.write("output.json", JSON.stringify(data));
// Check if file exists
const exists = await Bun.file("config.json").exists();
// Get file metadata
const { size, type, lastModified } = Bun.file("video.mp4");
Bun.serve (HTTP Server)
const server = Bun.serve({
port: 3000,
async fetch(req: Request): Promise<Response> {
const url = new URL(req.url);
if (url.pathname === "/api/hello") {
return Response.json({ message: "Hello from Bun!" });
}
// Serve static files
return new Response(Bun.file(`./public${url.pathname}`));
},
});
console.log(`Listening on ${server.url}`);
Bun's HTTP server benchmarks significantly faster than Node.js with Express, closer to Go or Rust HTTP servers in raw throughput.
Bun.spawn (Subprocess)
const proc = Bun.spawn(["ls", "-la"], {
stdout: "pipe",
cwd: "/tmp",
});
const output = await new Response(proc.stdout).text();
const exitCode = await proc.exited;
Bun.password (Hashing)
// Built-in bcrypt/argon2 — no bcryptjs package needed
const hash = await Bun.password.hash("my-password", {
algorithm: "bcrypt",
cost: 10,
});
const valid = await Bun.password.verify("my-password", hash);
Bun.env
// Access environment variables
const port = Bun.env.PORT ?? "3000";
// Bun auto-loads .env files — no dotenv package needed
SQLite
import { Database } from "bun:sqlite";
const db = new Database("app.db");
// Queries are synchronous (better for simple use cases)
db.run(`CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT)`);
const insert = db.prepare("INSERT INTO users (name) VALUES (?)");
insert.run("Alice");
const users = db.query("SELECT * FROM users").all();
// Type: { id: number, name: string }[]
Bun Test Runner
Built-in test runner compatible with Jest's API:
// test/user.test.ts
import { test, expect, describe, beforeEach } from "bun:test";
describe("User", () => {
let user: User;
beforeEach(() => {
user = new User("Alice", "[email protected]");
});
test("creates user with correct properties", () => {
expect(user.name).toBe("Alice");
expect(user.email).toBe("[email protected]");
});
test("validates email format", () => {
expect(() => new User("Bob", "invalid")).toThrow();
});
});
bun test # Run all tests
bun test --watch # Watch mode
bun test user.test.ts # Run specific file
bun test --coverage # Coverage report
Bundler
Bun includes a bundler for frontend and library builds:
# Bundle app for production
bun build ./src/index.ts --outdir ./dist --target browser
# Bundle as library
bun build ./src/index.ts --outfile dist/lib.js --format esm
// bun build API
await Bun.build({
entrypoints: ["./src/index.ts"],
outdir: "./dist",
target: "bun", // or "browser", "node"
minify: true,
sourcemap: "external",
});
For complex frontend builds with HMR, Vite is still the better choice. Bun's bundler is better suited for server-side bundles and simple frontend apps.
Node.js Compatibility
Bun implements most of the Node.js API and runtime behavior:
require()and CommonJS modules: ✓node:fs,node:path,node:crypto,node:http, etc.: ✓- Worker threads: ✓
__dirname,__filename: ✓- npm packages: most work without changes
Known gaps (2024):
- Some native Node.js addons (N-API) may not work
- Certain cluster/child_process behaviors differ
- Some edge cases in streams
For most web APIs, servers, CLIs, and scripts: Bun is drop-in compatible. For very complex Node.js applications or those relying on native addons, test before migrating.
When to Use Bun
Good fit:
- New TypeScript projects (no compilation step, fast startup)
- CLI tools (fast startup makes a real difference)
- REST APIs with straightforward logic
- Scripts and automation
- Replacing npm/yarn/pnpm (works in any Node.js project)
Consider alternatives:
- Complex bundling needs (Vite is more mature)
- Heavy native addon usage
- Teams with strong Node.js expertise who need ecosystem certainty
- Production critical systems where Bun's relative immaturity is a risk
Package manager only: You can use bun install as just a package manager in a Node.js project. It's the fastest way to install dependencies regardless of what runtime you use.
# Use bun as package manager in a Node.js project
bun install # replaces npm install — generates bun.lockb
bun add react
node dist/index.js # still run with Node if needed
Bun is at v1.x and production-ready for most use cases. The team is actively closing Node.js compatibility gaps and the ecosystem is maturing quickly.