The Bun Runtime
Overview
Bun is a JavaScript runtime designed as a fast, all-in-one replacement for Node.js. It runs JavaScript and TypeScript directly without a build step, ships with a built-in package manager, bundler, and test runner, and starts up noticeably faster than Node.js. The runtime aims for Node.js API compatibility so existing code often runs with minimal changes.
Running Code with Bun
The simplest way to run code:
bun run index.js
bun run index.ts
bun run index.tsx
Bun natively understands TypeScript and JSX — no transpilation step required:
// greeter.ts
function greet(name: string): string {
return `Hello, ${name}!`;
}
console.log(greeet("Bun"));
bun run greeter.ts
# Hello, Bun!
Without bun run, Bun also executes directly:
bun index.ts
Built-in HTTP Server
Bun has a fast HTTP server built in. Bun.serve() takes a handlers object:
Bun.serve({
port: 3000,
fetch(req) {
const url = new URL(req.url);
if (url.pathname === "/") {
return new Response("Welcome to Bun!");
}
if (url.pathname === "/api/users") {
return Response.json({ users: ["alice", "bob"] });
}
return new Response("Not found", { status: 404 });
},
});
console.log("Server running at http://localhost:3000");
Run it with bun run server.ts and it starts immediately.
Reading Files
Bun.file() creates a lazy file reader:
const file = Bun.file("data.json");
const contents = await file.text(); // string
const json = await file.json(); // parsed JSON
const buffer = await file.arrayBuffer(); // raw bytes
It doesn’t read the file until you call the accessor method. This is useful for serving static assets:
Bun.serve({
fetch(req) {
const url = new URL(req.url);
const file = Bun.file(`./public${url.pathname}`);
if (await file.exists()) {
return new Response(file);
}
return new Response("Not found", { status: 404 });
},
});
Bun.write for Fast File Output
const content = "Hello, Bun file writing!";
// Write string to file
await Bun.write("output.txt", content);
// Or use the file writer
const file = Bun.file("data.json");
await Bun.write(file, JSON.stringify({ hello: "world" }));
Environment Variables
Load .env files automatically:
# .env
DATABASE_URL=postgres://localhost/mydb
PORT=3000
// Access like Node.js process.env
console.log(process.env.DATABASE_URL);
console.log(process.env.PORT);
Bun reads .env files at startup and populates process.env.
Hot Reload
Bun watches for file changes and restarts automatically when running with --watch:
bun --watch server.ts
Changes to .ts, .tsx, .js, and .json files trigger a restart. This is useful during development without needing a separate tool like nodemon.
Running Scripts from package.json
Bun runs package.json scripts just like npm:
bun run dev
bun run build
bun run test
If you have both a dev script and a dev.ts file, bun run dev prioritizes the script defined in package.json:
{
"scripts": {
"dev": "bun --watch server.ts"
}
}
Testing with Bun
Bun ships a built-in test runner that is Jest-compatible:
// math.test.ts
import { describe, test, expect } from "bun:test";
function add(a: number, b: number): number {
return a + b;
}
describe("add", () => {
test("adds two numbers", () => {
expect(add(2, 3)).toBe(5);
});
test("handles negative numbers", () => {
expect(add(-1, 1)).toBe(0);
});
});
Run tests:
bun test
bun test --coverage
The test runner supports describe, test, it, beforeEach, afterEach, and the full expect API from Jest. No configuration needed.
Built-in SQLite
Bun includes a fast SQLite driver:
import { Database } from "bun:sqlite";
const db = new Database("app.db");
// Create a table
db.run(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE
)
`);
// Insert data
db.run("INSERT INTO users (name, email) VALUES (?, ?)", "Alice", "alice@example.com");
// Query data
const users = db.query("SELECT * FROM users WHERE name = ?").all("Alice");
console.log(users);
Bun vs Node.js Compatibility
Bun aims to be a drop-in replacement for Node.js. Most npm packages work, and Node.js built-in modules (fs, path, crypto, Buffer, process, stream) are available:
import { readFileSync } from "fs";
import { join } from "path";
const config = readFileSync(join(__dirname, "config.json"), "utf-8");
Where Bun diverges from Node.js:
| Feature | Bun | Node.js |
|---|---|---|
| TypeScript | Native, no config | Requires ts-node or compilation |
| JSX | Native | Requires a build step |
| npm packages | Works | Works |
node_modules resolution | Fast | Standard |
| Built-in test runner | Jest-compatible | External (Jest/Vitest) |
| Hot reload | --watch flag | nodemon or similar |
| Startup time | ~3x faster | Slower |
Using Bun’s Package Manager
Bun’s package manager is significantly faster than npm or yarn:
bun install
bun add express
bun add -d typescript @types/node
bun remove lodash
bun install reads from package.json and bun.lockb (Bun’s lockfile). It falls back to package-lock.json if no lockfile exists.
Common Flags
bun run --watch server.ts # Hot reload
bun run --cwd /path/to/project # Change working directory
bun build ./entry.ts --outdir ./dist # Bundle
bun test --reporter=verbose # Verbose test output
See Also
- /guides/javascript-event-loop/ — how JavaScript runtimes handle async operations, relevant to Bun’s single-threaded model
- /guides/javascript-vite-guide/ — Vite as an alternative bundler/dev server, sometimes used alongside Bun
- /tutorials/node-getting-started/ — getting started with Node.js, for context on the ecosystem Bun replaces