Skip to content

Bun Reference: Runtime, HTTP Server, Package Manager, Bundler & Built-in SQLite

Bun is a fast JavaScript runtime built on JavaScriptCore (not V8). It’s faster than Node.js for most workloads, drops-in as a Node.js replacement, and bundles a package manager, bundler, and test runner. The practical migration path: use Bun as a drop-in for Node.js scripts first, then adopt bun install (25x faster than npm), and finally evaluate Bun APIs like Bun.serve() for new services. Most npm packages work. The ones that don’t are packages with native Node.js C++ addons or ones that depend on undocumented Node.js internals.

1. Runtime & Node.js Compatibility

Drop-in Node.js replacement, native APIs, bun vs node performance gotchas
# Install:
curl -fsSL https://bun.sh/install | bash
# Or: brew install oven-sh/bun/bun

# Drop-in Node.js replacement:
bun run index.ts      # TypeScript natively, no tsc needed
bun run server.js     # same as node server.js
bun src/app.ts        # shorthand

# package.json scripts work as-is:
bun run dev
bun run build
bun run test

# Most Node.js built-ins are supported:
import { readFile, writeFile } from "fs/promises";
import { createServer } from "http";
import { join } from "path";
import { createHash } from "crypto";

// Bun-native alternatives are faster — prefer when not targeting Node.js:
const file = Bun.file("./data.json");       // BunFile — lazy, no memory until read
const text = await file.text();
const json = await file.json();
const bytes = await file.arrayBuffer();

await Bun.write("output.txt", "Hello world");        // atomic write
await Bun.write("output.json", JSON.stringify(data)); // auto-stringify

// Environment variables:
const port = Bun.env.PORT ?? "3000";        // Bun.env is process.env
const secret = Bun.env.SECRET_KEY!;        // throws if undefined at runtime? No — but TypeScript complains

// Process:
console.log(Bun.version);     // "1.1.x"
console.log(process.version); // "v22.x.x" (Node.js compatibility shim)

2. HTTP Server — Bun.serve()

Built-in HTTP server, routing, WebSockets, and TLS — no Express needed
// Built-in HTTP server (Bun.serve wraps WebKit's HTTP parser):
const server = Bun.serve({
  port: 3000,
  hostname: "0.0.0.0",

  async fetch(req: Request): Promise<Response> {
    const url = new URL(req.url);

    // Routing:
    if (url.pathname === "/health") {
      return Response.json({ status: "ok" });
    }

    if (url.pathname === "/users" && req.method === "GET") {
      const users = await db.user.findMany();
      return Response.json(users);
    }

    if (url.pathname === "/users" && req.method === "POST") {
      const body = await req.json();
      const user = await db.user.create({ data: body });
      return Response.json(user, { status: 201 });
    }

    return new Response("Not Found", { status: 404 });
  },

  error(error: Error): Response {
    console.error(error);
    return new Response("Internal Server Error", { status: 500 });
  },
});
console.log(`Listening on ${server.url}`);

// WebSockets (built-in, no ws package needed):
const wsServer = Bun.serve({
  fetch(req, server) {
    if (server.upgrade(req)) return;  // upgrade to WebSocket
    return new Response("Upgrade failed", { status: 500 });
  },
  websocket: {
    message(ws, message) {
      ws.send(`Echo: ${message}`);
      ws.publish("chat", message);   // broadcast to topic
    },
    open(ws) {
      ws.subscribe("chat");
    },
    close(ws, code, message) {
      ws.unsubscribe("chat");
    },
  },
});

// TLS:
Bun.serve({
  tls: { key: Bun.file("key.pem"), cert: Bun.file("cert.pem") },
  fetch: handler,
});

3. Package Manager — bun install & Workspaces

25x faster than npm, lockfile format, workspaces, and bun add/remove/update
# bun install — reads package.json, writes bun.lockb (binary lockfile):
bun install           # install all deps (25x faster than npm)
bun install --frozen  # CI: fail if lockfile would change (like npm ci)
bun install --production  # skip devDependencies

bun add react                    # add to dependencies
bun add -d typescript            # add to devDependencies
bun add react@18                 # pin version
bun add github:user/repo         # from GitHub
bun remove react                 # uninstall
bun update react                 # update to latest compatible

# Run scripts without npx:
bunx create-next-app@latest .    # bunx = bun x (temp install + run)
bunx tsc --noEmit

# Workspaces (monorepo):
# package.json (root):
# { "workspaces": ["packages/*", "apps/*"] }
bun install     # installs all workspace deps, creates shared node_modules
bun run --filter "*" build   # run build in all workspaces

# bun.lockb is binary — commit it. To see human-readable diff:
bun bun.lockb   # prints as JSON

# Override a package version (e.g. deduplicate or patch):
# package.json:
# { "overrides": { "semver": "7.5.4" } }

# Link local package for development:
# In the package: bun link
# In the consumer: bun link my-package

4. Bundler & bun build

Built-in bundler, target options, plugins, and tree-shaking
// bun build — bundles for browser, Node.js, or Bun:
await Bun.build({
  entrypoints: ["./src/index.ts"],
  outdir: "./dist",
  target: "browser",    // or "node", "bun"
  format: "esm",        // or "cjs", "iife"
  splitting: true,       // code splitting for browser
  sourcemap: "external",
  minify: true,
  define: {
    "process.env.NODE_ENV": JSON.stringify("production"),
    "__VERSION__": JSON.stringify("1.0.0"),
  },
  // External packages (don't bundle):
  external: ["react", "react-dom"],
});

// CLI equivalent:
// bun build ./src/index.ts --outdir ./dist --target browser --minify

// Plugin API (Bun-native plugins, or Esbuild-compatible):
import type { BunPlugin } from "bun";
const myPlugin: BunPlugin = {
  name: "My Plugin",
  setup(build) {
    build.onLoad({ filter: /\.txt$/ }, async ({ path }) => ({
      contents: `export default ${JSON.stringify(await Bun.file(path).text())}`,
      loader: "js",
    }));
  },
};
Bun.build({ plugins: [myPlugin], ... });

// CSS is bundled automatically (no extra config):
// import "./styles.css" works in browser builds
// CSS Modules: import styles from "./Component.module.css"

5. Test Runner & SQLite

Built-in test runner (Jest-compatible), bun:sqlite for zero-dependency SQLite
// bun test — Jest-compatible test runner (no config needed):
import { describe, it, expect, mock, beforeAll } from "bun:test";

describe("UserService", () => {
  it("creates a user", async () => {
    const user = await UserService.create({ name: "Alice" });
    expect(user.id).toBeDefined();
    expect(user.name).toBe("Alice");
  });

  it("throws on duplicate email", async () => {
    await expect(async () => {
      await UserService.create({ email: "dupe@example.com" });
      await UserService.create({ email: "dupe@example.com" });
    }).toThrow("duplicate");
  });
});

// Mock (Bun-native):
const mockFn = mock(() => 42);
mockFn();
expect(mockFn).toHaveBeenCalledTimes(1);

// Run tests:
// bun test                    — all *.test.ts files
// bun test --watch            — watch mode
// bun test --coverage         — coverage report
// bun test src/utils.test.ts  — specific file

// bun:sqlite — zero-dependency SQLite (no better-sqlite3 needed):
import { Database } from "bun:sqlite";

const db = new Database("mydb.sqlite");
// In-memory: new Database(":memory:")

db.exec("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT)");

// Prepared statements (use for repeated queries):
const insert = db.prepare("INSERT INTO users (name) VALUES ($name) RETURNING *");
const user = insert.get({ $name: "Alice" }) as { id: number; name: string };

const select = db.prepare("SELECT * FROM users WHERE id = $id");
const found = select.get({ $id: user.id });

// Transaction:
const tx = db.transaction((users: string[]) => {
  for (const name of users) insert.run({ $name: name });
});
tx(["Bob", "Carol", "Dave"]);   // atomic

db.close();

Track Node.js and JavaScript runtime releases at ReleaseRun. Related: TypeScript Reference | Deno 2 Reference | SQLite Reference | Nodejs EOL Tracker

🔍 Free tool: npm Package Health Checker — for npm-compatible packages in Bun, check any package for EOL status, known CVEs, and active maintenance.

Founded

2023 in London, UK

Contact

hello@releaserun.com