NexusCS

Bun

JavaScript Runtimes
Fast all-in-one JavaScript runtime with bundler, test runner, and package manager. Drop-in replacement for Node.js with native TypeScript support.
javascript
runtime
bundler
package-manager
typescript

Getting started

Installation

# macOS/Linux (curl)
curl -fsSL https://bun.sh/install | bash

# macOS (Homebrew)
brew install oven-sh/bun/bun

# Windows (PowerShell)
powershell -c "irm bun.sh/install.ps1 | iex"

# Docker
docker pull oven/bun
docker run --rm oven/bun --version

# Upgrade to latest
bun upgrade

Quick Start

# Initialize new project
bun init

# Run TypeScript directly
bun index.ts

# Run with watch mode
bun --watch index.ts

# Hot reload mode
bun --hot index.ts

First Script

// server.ts
const server = Bun.serve({
  port: 3000,
  fetch(req) {
    return new Response("Hello from Bun!");
  },
});

console.log(`Listening on ${server.url}`);
bun server.ts
# ⚡ 3x faster startup than Node.js

Running Code

Basic Execution

# Run JavaScript
bun run index.js

# Run TypeScript (no compilation needed)
bun run index.ts

# Run package.json script
bun run dev
bun run build
bun run test

# Short form
bun dev
bun build
bun test

Watch Mode

# Auto-restart on file changes
bun --watch server.ts

# Watch with specific files
bun --watch src/**/*.ts server.ts

# Hot reload (faster, no restart)
bun --hot server.ts

REPL

# Interactive shell
bun repl

# Execute and print
bun -e "console.log('Hello')"

# Pipe input
echo "2 + 2" | bun -

Package Management

Installing Packages

# Install from package.json
bun install

# Add package
bun add react
bun add -d typescript
bun add --dev vitest

# Add specific version
bun add react@18.2.0

# Add from GitHub
bun add github:user/repo

# Global install
bun add -g typescript

Removing Packages

# Remove package
bun remove react

# Remove dev dependency
bun remove -d typescript

Package Info

# List installed packages
bun pm ls

# Check outdated
bun outdated

# Clean cache
bun pm cache rm

bunx (npx alternative)

# Run package without installing
bunx create-react-app my-app
bunx tsc --init
bunx cowsay "Hello Bun!"

# Faster than npx - no install delay

Bundler

Basic Bundling

# Bundle single file
bun build ./index.ts --outdir ./dist

# Bundle with minification
bun build ./index.ts --minify --outfile ./dist/app.js

# Watch mode
bun build ./index.ts --watch --outdir ./dist

Advanced Options

# Target platforms
bun build --target browser ./index.ts
bun build --target node ./index.ts
bun build --target bun ./index.ts

# Splitting
bun build --splitting ./index.ts

# Source maps
bun build --sourcemap ./index.ts

# Multiple entry points
bun build ./src/*.ts --outdir ./dist

Build Configuration

// build.ts
await Bun.build({
  entrypoints: ["./index.ts"],
  outdir: "./dist",
  target: "browser",
  minify: true,
  splitting: true,
  sourcemap: "external",
  external: ["react", "react-dom"],
});

Performance

Tool Build Time
Bun 0.1s
esbuild 0.2s
Webpack 2.5s
Parcel 3.1s

Testing

Basic Testing

// math.test.ts
import { expect, test, describe } from "bun:test";

describe("math", () => {
  test("addition", () => {
    expect(2 + 2).toBe(4);
  });

  test("subtraction", () => {
    expect(5 - 3).toBe(2);
  });
});

Running Tests

# Run all tests
bun test

# Watch mode
bun test --watch

# Run specific file
bun test math.test.ts

# Run with coverage
bun test --coverage

# Bail on first failure
bun test --bail

Matchers

import { expect, test } from "bun:test";

test("matchers", () => {
  // Equality
  expect(value).toBe(4);
  expect(obj).toEqual({ a: 1 });

  // Truthiness
  expect(value).toBeTruthy();
  expect(value).toBeFalsy();
  expect(value).toBeNull();
  expect(value).toBeUndefined();

  // Numbers
  expect(value).toBeGreaterThan(3);
  expect(value).toBeLessThanOrEqual(5);
  expect(0.1 + 0.2).toBeCloseTo(0.3);

  // Strings
  expect(str).toMatch(/pattern/);
  expect(str).toContain("substring");

  // Arrays
  expect(arr).toContain("item");
  expect(arr).toHaveLength(3);

  // Promises
  await expect(promise).resolves.toBe(value);
  await expect(promise).rejects.toThrow();
});

Lifecycle Hooks

import { beforeAll, beforeEach, afterEach, afterAll, test } from "bun:test";

beforeAll(() => {
  // Run once before all tests
});

beforeEach(() => {
  // Run before each test
});

afterEach(() => {
  // Run after each test
});

afterAll(() => {
  // Run once after all tests
});

test("with setup/teardown", () => {
  // Test code
});

Mocking

import { mock, spyOn, test, expect } from "bun:test";

test("mock function", () => {
  const fn = mock(() => "mocked");
  expect(fn()).toBe("mocked");
  expect(fn).toHaveBeenCalled();
  expect(fn).toHaveBeenCalledTimes(1);
});

test("spy on method", () => {
  const obj = { method: () => "original" };
  const spy = spyOn(obj, "method");

  obj.method();
  expect(spy).toHaveBeenCalled();

  spy.mockRestore();
});

Bun APIs

HTTP Server (Bun.serve)

// Fast HTTP server
const server = Bun.serve({
  port: 3000,
  fetch(req) {
    const url = new URL(req.url);

    if (url.pathname === "/") {
      return new Response("Home");
    }

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

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

console.log(`Server: ${server.url}`);

File I/O (Bun.file)

// Read file
const file = Bun.file("package.json");
const text = await file.text();
const json = await file.json();
const buffer = await file.arrayBuffer();

// Check file exists
const exists = await file.exists();

// File metadata
const size = file.size;
const type = file.type;

// Stream file
const stream = file.stream();

Writing Files (Bun.write)

// Write string
await Bun.write("output.txt", "Hello Bun!");

// Write JSON
await Bun.write("data.json", JSON.stringify({ key: "value" }));

// Write Response
const response = await fetch("https://example.com");
await Bun.write("page.html", response);

// Write with options
await Bun.write("file.txt", "content", {
  createPath: true,
});

Hashing (Bun.hash)

// Fast hashing
import { hash } from "bun";

// Wyhash (default, fastest)
hash("Hello Bun!"); // Returns number
hash.wyhash("Hello Bun!");

// CityHash
hash.cityHash64("Hello Bun!");

// Murmur
hash.murmur32v3("Hello Bun!");
hash.murmur64v2("Hello Bun!");

Password Hashing

// Bcrypt
const password = "secret123";
const hashed = await Bun.password.hash(password);
const isValid = await Bun.password.verify(password, hashed);

// Argon2
const hashed2 = await Bun.password.hash(password, {
  algorithm: "argon2id",
  memoryCost: 65536,
  timeCost: 3,
});

Environment Variables

// Access env vars
const apiKey = Bun.env.API_KEY;
const port = Bun.env.PORT || 3000;

// Load .env file (automatic)
// .env file is loaded by default
console.log(Bun.env.DATABASE_URL);

// All env vars
console.log(Bun.env);

Process Info

// Bun version
console.log(Bun.version);

// Bun revision
console.log(Bun.revision);

// Main file path
console.log(Bun.main);

// Platform
console.log(process.platform);
console.log(process.arch);

TypeScript

Built-in Support

// No configuration needed
// Just run TypeScript files directly

// server.ts
interface User {
  id: number;
  name: string;
}

const users: User[] = [{ id: 1, name: "Alice" }];

Bun.serve({
  fetch() {
    return Response.json(users);
  },
});
# No tsc, no compilation step
bun server.ts

TypeScript Config

// tsconfig.json (optional)
{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "lib": ["ESNext"],
    "types": ["bun-types"],
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "noEmit": true,
    "strict": true
  }
}

Type Definitions

# Install Bun types
bun add -d bun-types

# Add to tsconfig.json
# "types": ["bun-types"]
// Now get full IDE support
const server = Bun.serve({
  // Full autocomplete for options
});

JSX Support

// Automatic JSX support
const element = <div>Hello</div>;

// Configure in tsconfig.json
{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "react"
  }
}

Web APIs

Fetch API

// Built-in fetch (fast)
const response = await fetch("https://api.example.com/data");
const data = await response.json();

// With options
const response = await fetch("https://api.example.com/users", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ name: "Alice" }),
});

// FormData
const formData = new FormData();
formData.append("file", file);
await fetch("/upload", {
  method: "POST",
  body: formData,
});

WebSocket

// Client
const ws = new WebSocket("ws://localhost:3000");

ws.addEventListener("open", () => {
  ws.send("Hello Server!");
});

ws.addEventListener("message", (event) => {
  console.log("Message:", event.data);
});

// Server
Bun.serve({
  fetch(req, server) {
    if (server.upgrade(req)) {
      return; // Upgraded to WebSocket
    }
    return new Response("Upgrade failed", { status: 500 });
  },
  websocket: {
    open(ws) {
      console.log("Client connected");
    },
    message(ws, message) {
      ws.send(`Echo: ${message}`);
    },
    close(ws) {
      console.log("Client disconnected");
    },
  },
});

Streams

// ReadableStream
const stream = new ReadableStream({
  start(controller) {
    controller.enqueue("chunk 1");
    controller.enqueue("chunk 2");
    controller.close();
  },
});

// Stream response
Bun.serve({
  fetch() {
    return new Response(stream);
  },
});

// File streaming
const file = Bun.file("large.json");
return new Response(file.stream());

Web Crypto

// Generate random values
const array = new Uint8Array(16);
crypto.getRandomValues(array);

// UUID
const uuid = crypto.randomUUID();

// Hashing
const data = new TextEncoder().encode("Hello");
const hash = await crypto.subtle.digest("SHA-256", data);

// HMAC
const key = await crypto.subtle.generateKey(
  { name: "HMAC", hash: "SHA-256" },
  false,
  ["sign", "verify"],
);

Node.js Compatibility

Module System

// ESM (preferred)
import fs from "fs";
import { readFile } from "fs/promises";

// CommonJS (supported)
const fs = require("fs");

// node: prefix (recommended)
import fs from "node:fs";
import path from "node:path";

Core Modules

// Most Node modules work
import fs from "node:fs/promises";
import path from "node:path";
import { Buffer } from "node:buffer";
import crypto from "node:crypto";
import http from "node:http";

// Use Bun APIs when available (faster)
// ✗ const data = await fs.readFile("file.txt");
// ✓ const data = await Bun.file("file.txt").text();

Key Differences

Feature Node.js Bun
TypeScript Needs compilation Native support
Package manager npm/yarn/pnpm Built-in (faster)
Test runner Jest/Vitest Built-in (faster)
Bundler Webpack/esbuild Built-in (faster)
.env loading Needs dotenv Automatic
Watch mode Needs nodemon Built-in --watch

npm Compatibility

# Most npm packages work
bun add express
bun add react react-dom

# Some packages need adjustments
# Check Bun compatibility: bun.sh/guides/ecosystem

Migration Tips

// Replace Node-specific code

// Node.js
import { readFile } from "fs/promises";
const data = await readFile("file.txt", "utf8");

// Bun (faster)
const data = await Bun.file("file.txt").text();

// Node.js
import { createServer } from "http";
const server = createServer((req, res) => {
  res.end("Hello");
});

// Bun (faster)
Bun.serve({
  fetch(req) {
    return new Response("Hello");
  },
});

Performance

Speed Comparisons

Task Node.js Bun Speedup
Cold start 100ms 30ms 3.3x
bun install 15s 1s 15x
HTTP req/sec 50k 130k 2.6x
FFI calls 1M/s 10M/s 10x
Bundling 2.5s 0.1s 25x

Optimization Tips

// 1. Use Bun APIs over Node.js APIs
// ✓ Fast
await Bun.file("data.json").json();

// ✗ Slower
await fs.readFile("data.json", "utf8").then(JSON.parse);

// 2. Use Bun.serve over http.createServer
// ✓ 2.6x faster
Bun.serve({
  fetch(req) {
    return new Response();
  },
});

// ✗ Slower
http.createServer((req, res) => {
  res.end();
});

// 3. Use Bun.write for file operations
// ✓ Optimized
await Bun.write("out.txt", "data");

// ✗ Slower
await fs.writeFile("out.txt", "data");

Memory Usage

// Efficient file streaming
const file = Bun.file("large.mp4");

// Stream to response (low memory)
return new Response(file.stream());

// Don't load entire file
// ✗ High memory
const buffer = await file.arrayBuffer();
return new Response(buffer);

Benchmarking

// Built-in performance API
const start = performance.now();

// Code to benchmark
await heavyOperation();

const end = performance.now();
console.log(`Took ${end - start}ms`);

// Console timing
console.time("operation");
await operation();
console.timeEnd("operation");

Package Configuration

package.json Scripts

{
  "name": "my-app",
  "version": "1.0.0",
  "scripts": {
    "dev": "bun --watch index.ts",
    "build": "bun build index.ts --outdir dist --minify",
    "start": "bun index.ts",
    "test": "bun test",
    "test:watch": "bun test --watch"
  },
  "dependencies": {
    "express": "^4.18.2"
  },
  "devDependencies": {
    "bun-types": "latest"
  }
}

bunfig.toml

# Bun configuration file

[install]
# Registry configuration
registry = "https://registry.npmjs.org/"
# Scoped registries
# registry = { "https://npm.local.com": { token: "123" }}

# Cache directory
cache = "~/.bun/cache"

# Optional peer dependencies
optional = true

# Install exact versions
exact = true

[test]
# Test runner configuration
coverage = true
coverageThreshold = 80

[run]
# Shell to use for scripts
shell = "bash"

Workspaces

// package.json
{
  "name": "monorepo",
  "workspaces": ["packages/*"]
}
# Install all workspace dependencies
bun install

# Run script in workspace
bun run --filter @myorg/package-a build

# Add dependency to workspace
bun add react --filter @myorg/package-b

Advanced Features

FFI (Foreign Function Interface)

import { dlopen, FFIType, suffix } from "bun:ffi";

// Load native library
const lib = dlopen(`libsqlite3.${suffix}`, {
  sqlite3_libversion: {
    returns: FFIType.cstring,
    args: [],
  },
});

const version = lib.symbols.sqlite3_libversion();
console.log(version);

SQLite (Built-in)

import { Database } from "bun:sqlite";

// Open database
const db = new Database("mydb.sqlite");

// Create table
db.run(`
  CREATE TABLE IF NOT EXISTS users (
    id INTEGER PRIMARY KEY,
    name TEXT
  )
`);

// Insert
db.run("INSERT INTO users (name) VALUES (?)", ["Alice"]);

// Query
const users = db.query("SELECT * FROM users").all();
console.log(users);

// Prepared statements
const insert = db.prepare("INSERT INTO users (name) VALUES (?)");
insert.run("Bob");
insert.run("Charlie");

// Close
db.close();

TCP Sockets

// TCP server
const server = Bun.listen({
  hostname: "localhost",
  port: 8080,
  socket: {
    data(socket, data) {
      socket.write(data); // Echo
    },
    open(socket) {
      console.log("Connected");
    },
    close(socket) {
      console.log("Disconnected");
    },
  },
});

// TCP client
const socket = await Bun.connect({
  hostname: "localhost",
  port: 8080,
  socket: {
    data(socket, data) {
      console.log("Received:", data);
    },
  },
});

socket.write("Hello Server!");

Transpiler

// Transpile TypeScript to JavaScript
const transpiler = new Bun.Transpiler({
  loader: "tsx",
});

const code = transpiler.transformSync(`
  const x: number = 42;
  console.log(x);
`);

console.log(code);
// const x = 42;
// console.log(x);

CLI Commands

Help & Info

# Version
bun --version
bun -v

# Help
bun --help
bun run --help

# Completions
bun completions

# Upgrade
bun upgrade

Project Commands

# Initialize project
bun init

# Run script
bun run start
bun start  # short form

# Execute file
bun index.ts

# REPL
bun repl

Build Commands

# Bundle
bun build ./index.ts --outdir ./dist

# Watch
bun build --watch ./index.ts

# Minify
bun build --minify ./index.ts

# Target
bun build --target=browser ./index.ts
bun build --target=node ./index.ts
bun build --target=bun ./index.ts

Package Commands

# Install
bun install
bun i  # short form

# Add
bun add <package>
bun add -d <package>  # dev
bun add -g <package>  # global

# Remove
bun remove <package>

# Update
bun update
bun update <package>

# Link
bun link
bun link <package>

Test Commands

# Run tests
bun test

# Watch mode
bun test --watch

# Specific file
bun test file.test.ts

# Coverage
bun test --coverage

# Bail on failure
bun test --bail

Debugging

Debug Mode

# Enable debug logging
bun --debug index.ts

# Verbose logging
bun --verbose index.ts

# Log level
bun --log-level=debug index.ts

Node Inspector

# Start with inspector
bun --inspect index.ts

# With breakpoint
bun --inspect-brk index.ts

# Custom port
bun --inspect=9229 index.ts

Environment Variables

# Debug Bun itself
BUN_DEBUG_QUIET_LOGS=1 bun run index.ts

# Disable cache
BUN_DISABLE_CACHE=1 bun install

# Force IPv4
BUN_FORCE_IPV4=1 bun run server.ts

Error Handling

// Stack traces include source maps
try {
  throw new Error("Oops");
} catch (error) {
  console.error(error);
  // Points to TypeScript line numbers
}

// Unhandled promise rejections
process.on("unhandledRejection", (reason) => {
  console.error("Unhandled:", reason);
});

Docker

Dockerfile

# Official Bun image
FROM oven/bun:1

# Set working directory
WORKDIR /app

# Copy package files
COPY package.json bun.lockb ./

# Install dependencies
RUN bun install --frozen-lockfile

# Copy source
COPY . .

# Build if needed
RUN bun run build

# Expose port
EXPOSE 3000

# Start app
CMD ["bun", "run", "start"]

Multi-stage Build

# Build stage
FROM oven/bun:1 AS builder
WORKDIR /app
COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile
COPY . .
RUN bun run build

# Production stage
FROM oven/bun:1-slim
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package.json ./
EXPOSE 3000
CMD ["bun", "run", "start"]

Docker Compose

version: "3.8"

services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
    volumes:
      - .:/app
      - /app/node_modules
    command: bun --watch index.ts

Migration from Node.js

Step-by-Step Guide

# 1. Install Bun
curl -fsSL https://bun.sh/install | bash

# 2. Remove old dependencies
rm -rf node_modules package-lock.json

# 3. Install with Bun
bun install

# 4. Update scripts in package.json
# Change: "start": "node index.js"
# To:     "start": "bun index.js"

# 5. Test your app
bun run dev
bun test

# 6. Optional: Migrate to TypeScript
mv index.js index.ts
# Bun runs TypeScript directly!

Common Replacements

// __dirname and __filename (ESM)
// Node.js
import { fileURLToPath } from "url";
const __dirname = dirname(fileURLToPath(import.meta.url));

// Bun
const __dirname = import.meta.dir;
const __filename = import.meta.path;

// require (ESM)
// Node.js (need to use import)
// Bun
const data = require("./data.json"); // Works in ESM

// Environment variables
// Node.js
require("dotenv").config();

// Bun (automatic)
// Just use Bun.env or process.env

Performance Checklist

  • Replace fs.readFile with Bun.file().text()
  • Replace http.createServer with Bun.serve()
  • Use Bun.write() instead of fs.writeFile
  • Remove dotenv package (built-in)
  • Remove nodemon (use bun --watch)
  • Replace Jest with bun test
  • Use built-in bundler instead of Webpack
  • Use bunx instead of npx

Common Patterns

REST API

const server = Bun.serve({
  port: 3000,
  async fetch(req) {
    const url = new URL(req.url);

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

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

    // PUT /users/:id
    if (url.pathname.match(/^\/users\/\d+$/) && req.method === "PUT") {
      const id = url.pathname.split("/")[2];
      const body = await req.json();
      // Update user
      return Response.json(body);
    }

    // DELETE /users/:id
    if (url.pathname.match(/^\/users\/\d+$/) && req.method === "DELETE") {
      const id = url.pathname.split("/")[2];
      // Delete user
      return new Response(null, { status: 204 });
    }

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

File Upload

Bun.serve({
  async fetch(req) {
    if (req.method === "POST" && req.url.endsWith("/upload")) {
      const formData = await req.formData();
      const file = formData.get("file") as File;

      if (!file) {
        return new Response("No file", { status: 400 });
      }

      // Save file
      await Bun.write(`uploads/${file.name}`, file);

      return Response.json({
        name: file.name,
        size: file.size,
        type: file.type,
      });
    }

    return new Response("Method not allowed", { status: 405 });
  },
});

Static File Serving

const server = Bun.serve({
  port: 3000,
  async fetch(req) {
    const url = new URL(req.url);
    const filePath = `./public${url.pathname}`;

    const file = Bun.file(filePath);

    if (await file.exists()) {
      return new Response(file);
    }

    // Serve index.html for SPA
    if (url.pathname.startsWith("/app")) {
      return new Response(Bun.file("./public/index.html"));
    }

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

Middleware Pattern

type Handler = (req: Request) => Response | Promise<Response>;
type Middleware = (handler: Handler) => Handler;

// Logger middleware
const logger: Middleware = (handler) => {
  return async (req) => {
    console.log(`${req.method} ${req.url}`);
    const start = performance.now();
    const response = await handler(req);
    const duration = performance.now() - start;
    console.log(`${response.status} ${duration.toFixed(2)}ms`);
    return response;
  };
};

// Auth middleware
const auth: Middleware = (handler) => {
  return async (req) => {
    const token = req.headers.get("Authorization");
    if (!token) {
      return new Response("Unauthorized", { status: 401 });
    }
    return handler(req);
  };
};

// Compose middleware
const compose = (...middlewares: Middleware[]) => {
  return (handler: Handler): Handler => {
    return middlewares.reduceRight((h, m) => m(h), handler);
  };
};

// Use
const handler = compose(
  logger,
  auth,
)(async (req) => {
  return Response.json({ message: "Protected" });
});

Bun.serve({ fetch: handler });

Gotchas

CommonJS in ESM

Bun allows require() in ESM files, but this is non-standard:

// Works in Bun, breaks in Node.js
import { foo } from "./module.ts";
const bar = require("./legacy.js");

// Prefer standard ESM
import bar from "./legacy.js";

Top-level await

// Works in Bun (no need for async wrapper)
const data = await fetch("/api").then((r) => r.json());

// Node.js requires package.json type: "module"

Path resolution

// Bun resolves .ts extensions
import { func } from "./module.ts"; // OK in Bun

// Node.js requires:
import { func } from "./module.js"; // Even for .ts files

Global APIs

// Bun adds global APIs not in Node.js
console.log(Bun.version); // Bun only

// Use feature detection for compatibility
if (typeof Bun !== "undefined") {
  console.log("Running in Bun");
}

Package compatibility

Some npm packages may not work due to:

  • Native modules compiled for Node.js
  • Reliance on Node.js internals
  • Unsupported APIs

Check compatibility at bun.sh/guides/ecosystem

Hot reloading caveats

// Hot reload clears module cache but not memory
let cache = {}; // This persists across hot reloads

// Use --watch for full restarts
bun --watch index.ts

Also see