| name | bun |
| description | Bun runtime, package manager, bundler, and test runner.
Use when running scripts with bun, managing packages, serving HTTP with Bun.serve,
querying databases with Bun.sql/bun:sqlite/Bun.redis, shell scripting with $,
using S3/file I/O, writing tests with bun:test, bundling or compiling to executable,
or using any Bun-specific API (spawn, glob, semver, FFI, workers, plugins, HTMLRewriter).
|
Bun
Quick Start
curl -fsSL https://bun.sh/install | bash
bun init
bun run index.ts
bun --watch index.ts
bun --hot server.ts
Package Management
bun install
bun add express
bun add -d @types/node
bun remove lodash
bun update
bun update --latest
bunx prettier --write .
bun patch express
Workspaces
{ "workspaces": ["packages/*"] }
{ "dependencies": { "shared": "workspace:*" } }
bun install --filter "pkg-*"
Environment Variables
.env files auto-loaded in order: .env → .env.$(NODE_ENV) → .env.local
Bun.env.API_KEY
process.env.API_KEY
import.meta.env.API_KEY
bun --env-file=.env.staging run start
HTTP Server
Bun.serve({
port: 3000,
routes: {
"/": new Response("Home"),
"/users/:id": (req) => Response.json({ id: req.params.id }),
"/api/posts": {
GET: () => Response.json([]),
POST: async (req) => Response.json(await req.json(), { status: 201 }),
},
"/api/*": Response.json({ error: "Not found" }, { status: 404 }),
"/favicon.ico": Bun.file("./favicon.ico"),
},
fetch(req) {
return new Response("Not Found", { status: 404 });
},
});
WebSocket Upgrade
Bun.serve({
fetch(req, server) {
if (server.upgrade(req, { data: { userId: "123" } })) return;
return new Response("Not a WebSocket", { status: 400 });
},
websocket: {
open(ws) { ws.subscribe("chat"); },
message(ws, msg) { ws.publish("chat", msg); },
close(ws) {},
},
});
Fullstack (HTML Imports)
import homepage from "./index.html";
Bun.serve({
routes: { "/": homepage },
development: true,
});
See references/http-server.md for cookies, static routes, TLS, server lifecycle, metrics.
Databases
Bun.sql (Postgres / MySQL / SQLite)
import { sql, SQL } from "bun";
const users = await sql`SELECT * FROM users WHERE active = ${true}`;
const pg = new SQL("postgres://user:pass@localhost:5432/mydb");
const mysql = new SQL("mysql://user:pass@localhost:3306/mydb");
const sqlite = new SQL(":memory:");
const [user] = await sql`INSERT INTO users ${sql({ name: "Alice", email: "a@b.com" })} RETURNING *`;
await sql`INSERT INTO users ${sql([user1, user2, user3])}`;
await sql.begin(async (tx) => {
const [u] = await tx`INSERT INTO users ${sql({ name: "Bob" })} RETURNING *`;
await tx`INSERT INTO accounts (user_id) VALUES (${u.id})`;
});
bun:sqlite (Sync, Embedded)
import { Database } from "bun:sqlite";
const db = new Database("app.db");
db.run("CREATE TABLE IF NOT EXISTS kv (key TEXT PRIMARY KEY, val TEXT)");
const row = db.query("SELECT * FROM kv WHERE key = ?").get("foo");
const all = db.query("SELECT * FROM kv").all();
class User { id!: number; name!: string; }
const users = db.query("SELECT * FROM users").as(User).all();
Bun.redis
import { redis } from "bun";
await redis.set("key", "value", { ex: 60 });
const val = await redis.get("key");
await redis.del("key");
await redis.hmset("user:1", { name: "Alice", role: "admin" });
See references/database.md for transactions, savepoints, MySQL/SQLite specifics, Redis pub/sub, connection options.
Shell ($)
import { $ } from "bun";
await $`echo "Hello"`;
const text = await $`ls -la`.text();
const data = await $`cat config.json`.json();
for await (const line of $`cat file.txt`.lines()) { }
await $`cat file.txt | grep "pattern" | wc -l`;
await $`cat < ${new Response("data")} > ${Bun.file("out.txt")}`;
const { exitCode } = await $`may-fail`.nothrow().quiet();
$.cwd("/tmp");
$.env({ ...process.env, NODE_ENV: "production" });
Security: Interpolated variables are auto-escaped (no shell injection). Use { raw: str } to bypass.
See references/shell.md for builtins, command substitution, brace expansion.
File I/O & S3
const file = Bun.file("data.json");
const json = await file.json();
console.log(file.size, file.type);
await Bun.write("out.txt", "hello");
await Bun.write(Bun.file("copy.bin"), Bun.file("src.bin"));
const writer = Bun.file("log.txt").writer();
writer.write("line 1\n");
writer.write("line 2\n");
writer.end();
import { s3 } from "bun";
const obj = s3.file("data.json");
const data = await obj.json();
await obj.write(JSON.stringify({ ok: true }));
const url = obj.presign({ expiresIn: 3600, method: "PUT" });
await obj.delete();
const res = await fetch("s3://bucket/file.txt");
const glob = new Bun.Glob("**/*.ts");
for await (const path of glob.scan(".")) console.log(path);
See references/file-io.md for S3 credentials, multipart uploads, streams, hashing, semver.
Testing
import { test, expect, describe, mock, spyOn, beforeEach } from "bun:test";
describe("math", () => {
test("adds", () => expect(1 + 1).toBe(2));
test.each([
[1, 2, 3],
[4, 5, 9],
])("%i + %i = %i", (a, b, expected) => {
expect(a + b).toBe(expected);
});
test.skip("wip", () => {});
test.todo("implement later");
});
const fn = mock(() => 42);
fn();
expect(fn).toHaveBeenCalled();
mock.module("./db", () => ({
query: mock(() => []),
}));
const spy = spyOn(console, "log");
console.log("test");
expect(spy).toHaveBeenCalledWith("test");
test("snap", () => {
expect({ a: 1, b: "hello" }).toMatchSnapshot();
});
bun test
bun test --watch
bun test --coverage
bun test --update-snapshots
bun test --bail
bun test --test-name-pattern "add"
See references/testing.md for all matchers, lifecycle hooks, type testing, retry/repeats, DOM testing.
Bundler & Compile
const result = await Bun.build({
entrypoints: ["./src/index.ts"],
outdir: "./dist",
splitting: true,
minify: true,
sourcemap: "linked",
target: "browser",
});
if (!result.success) {
for (const log of result.logs) console.error(log);
}
bun build --compile --minify --sourcemap --bytecode ./app.ts --outfile myapp
bun build --compile --target=bun-linux-x64 ./app.ts --outfile myapp-linux
bun build --compile --target=bun-darwin-arm64 ./app.ts --outfile myapp-mac
import icon from "./icon.png" with { type: "file" };
import db from "./data.db" with { type: "sqlite", embed: "true" };
See references/bundler.md for all build options, plugins, cross-compile targets, Windows options, Transpiler API.
Child Processes
const proc = Bun.spawn(["ls", "-la"], {
cwd: "/tmp",
stdout: "pipe",
});
const output = await new Response(proc.stdout).text();
await proc.exited;
const { stdout, exitCode } = Bun.spawnSync(["echo", "hi"]);
const ctrl = new AbortController();
Bun.spawn(["sleep", "100"], { signal: ctrl.signal, timeout: 5000 });
const child = Bun.spawn(["bun", "worker.ts"], {
ipc(msg) { console.log("from child:", msg); },
});
child.send({ type: "start" });
Configuration (bunfig.toml)
preload = ["./setup.ts"]
logLevel = "warn"
[run]
shell = "bun"
bun = true
[test]
preload = ["./test-setup.ts"]
coverage = true
coverageThreshold = 0.8
retry = 2
[install]
exact = true
frozenLockfile = true
auto = "fallback"
See references/configuration.md for full bunfig.toml reference.
Reference Index
| Topic | Reference |
|---|
| HTTP server, routing, WebSockets, cookies, fullstack | references/http-server.md |
| Bun.sql, bun:sqlite, Bun.redis | references/database.md |
| TCP, UDP, DNS, fetch | references/networking.md |
| Bun Shell ($) | references/shell.md |
| bun:test, mocking, snapshots, coverage | references/testing.md |
| Bun.build, compile to executable, plugins | references/bundler.md |
| Bun.file, S3, Glob, streams, hashing, semver | references/file-io.md |
| bunfig.toml full reference | references/configuration.md |
| Workers, HTMLRewriter, FFI, C compiler, secrets, Node.js compat | references/advanced.md |