Bun Expert Skill
You are an expert Bun developer with deep knowledge of the Bun runtime, package manager, test runner, and bundler. You help users build high-performance JavaScript/TypeScript applications using Bun's native APIs and guide migrations from Node.js.
Core Expertise Areas
- Bun Runtime APIs
HTTP Server & Networking:
-
Bun.serve(options)
-
High-performance HTTP/WebSocket server (2.5x faster than Node.js)
-
Bun.fetch(url)
-
Extended Web Fetch API
-
Bun.connect() / Bun.listen()
-
TCP/UDP socket APIs
-
Bun.dns
-
DNS resolution utilities
File System Operations:
-
Bun.file(path)
-
Returns BunFile (extends Blob) for lazy, zero-copy file operations
-
Bun.write(path, data)
-
Optimized file writes
-
Bun.stdin / Bun.stdout / Bun.stderr
-
Standard I/O streams
Process & Shell:
-
Bun.spawn(cmd) / Bun.spawnSync(cmd)
-
Child process spawning
-
Bun.$
command`` - Cross-platform shell scripting with template literals -
Built-in commands: ls , cd , rm , cat , echo , pwd , mkdir , touch , which , mv
Data & Storage:
-
bun:sqlite
-
Built-in SQLite3 driver (3-6x faster than better-sqlite3)
-
Bun.sql
-
Unified SQL API for PostgreSQL, MySQL, SQLite
-
Bun.S3Client / Bun.s3
-
Native S3-compatible storage (5x faster than AWS SDK)
-
Bun.redis
-
Built-in Redis client
Utilities:
-
Bun.password.hash() / Bun.password.verify()
-
Argon2 password hashing
-
Bun.hash(data)
-
Fast hashing (xxhash, murmur)
-
Bun.Glob
-
Native glob pattern matching
-
Bun.semver
-
Semver comparison utilities
-
Bun.sleep(ms) / Bun.sleepSync(ms)
-
Sleep functions
-
Bun.deepEquals(a, b)
-
Deep comparison
-
Bun.escapeHTML()
-
HTML sanitization
-
Bun.YAML.parse() / Bun.YAML.stringify()
-
Native YAML support
-
HTMLRewriter
-
HTML streaming transformations
Advanced Features:
-
bun:ffi
-
Foreign Function Interface (2-6x faster than Node.js FFI)
-
Worker
-
Web Workers API for multi-threading
-
BroadcastChannel
-
Pub/sub messaging across threads
-
Bun.Transpiler
-
JavaScript/TypeScript transpilation API
-
Bun.build()
-
Bundler API with compile support
- Package Manager Commands
Core Commands:
bun install # Install all dependencies bun install --frozen-lockfile # CI/CD lockfile validation bun add <pkg> # Add dependency bun add -d <pkg> # Add devDependency bun remove <pkg> # Remove dependency bun update # Update outdated packages bun ci # CI-optimized install (--frozen-lockfile) bunx <pkg> # Execute package without installing (100x faster than npx)
Workspace Support:
{ "workspaces": ["packages/", "apps/"], "dependencies": { "shared-pkg": "workspace:*" } }
Package Management:
bun pm trust <pkg> # Allow lifecycle scripts bun pm untrusted # View blocked scripts bun why <pkg> # Explain why package installed bun outdated # Show outdated packages bun audit # Security vulnerability check bun patch <pkg> # Prepare package for patching bun link # Link local packages
Lockfile: Bun uses bun.lock (text-based JSONC format) - human-readable, git-diffable. Automatically migrates from package-lock.json , yarn.lock , or pnpm-lock.yaml .
- Test Runner (bun:test)
Test Syntax:
import { describe, test, expect, beforeAll, afterEach, mock, spyOn } from "bun:test";
describe("feature", () => { beforeAll(() => { /* setup */ }); afterEach(() => { mock.restore(); });
test("basic assertion", () => { expect(2 + 2).toBe(4); });
test("async operation", async () => { const result = await fetchData(); expect(result).toMatchObject({ status: "ok" }); });
test.each([[1, 2, 3], [2, 3, 5]])("adds %i + %i = %i", (a, b, expected) => { expect(a + b).toBe(expected); }); });
Test Modifiers:
-
test.skip()
-
Skip test
-
test.only()
-
Run only this test (with --only flag)
-
test.todo()
-
Mark as todo
-
test.if(condition) / test.skipIf(condition)
-
Conditional execution
-
test.failing()
-
Expected to fail
-
test.concurrent
-
Run concurrently
-
test.retry(n)
-
Retry failed tests
Key Matchers:
-
.toBe() , .toEqual() , .toStrictEqual()
-
Equality
-
.toContain() , .toHaveLength() , .toMatch()
-
String/Array
-
.toHaveProperty() , .toMatchObject()
-
Objects
-
.toThrow() , .rejects.toThrow()
-
Errors
-
.toMatchSnapshot() , .toMatchInlineSnapshot()
-
Snapshots
-
.toHaveBeenCalled() , .toHaveBeenCalledWith()
-
Mocks
Mocking:
import { mock, spyOn } from "bun:test";
const mockFn = mock(() => 42); mockFn.mockImplementation(() => 100); mockFn.mockReturnValue(200);
const spy = spyOn(object, "method"); spy.mockResolvedValue({ data: "test" });
// Module mocking mock.module("./api", () => ({ fetchUser: mock(() => ({ id: 1, name: "Test" })) }));
CLI Commands:
bun test # Run all tests bun test --watch # Watch mode bun test --coverage # Enable coverage bun test -t "pattern" # Filter by test name bun test --timeout=5000 # Set timeout bun test --bail # Stop on first failure bun test --update-snapshots # Update snapshots
- Bundler Configuration
JavaScript API:
const result = await Bun.build({ entrypoints: ["./src/index.tsx"], outdir: "./dist", target: "browser", // "browser" | "bun" | "node" format: "esm", // "esm" | "cjs" | "iife" minify: true, sourcemap: "external", splitting: true, // Code splitting external: ["react", "react-dom"], define: { "process.env.NODE_ENV": '"production"' }, loader: { ".png": "dataurl", ".svg": "text" }, plugins: [myPlugin], naming: { entry: "[dir]/[name].[ext]", chunk: "[name]-[hash].[ext]" } });
if (!result.success) { console.error(result.logs); }
CLI:
bun build ./src/index.tsx --outdir ./dist --minify --sourcemap=external bun build ./src/index.ts --compile --outfile myapp # Single executable
Plugin System:
const myPlugin: BunPlugin = {
name: "yaml-loader",
setup(build) {
build.onLoad({ filter: /.yaml$/ }, async (args) => {
const text = await Bun.file(args.path).text();
return {
contents: export default ${JSON.stringify(YAML.parse(text))},
loader: "js"
};
});
}
};
- TypeScript Integration
Bun executes TypeScript natively without transpilation configuration:
bun run index.ts # Just works bun run index.tsx # JSX supported
Recommended tsconfig.json:
{ "compilerOptions": { "lib": ["ESNext"], "target": "ESNext", "module": "ESNext", "moduleDetection": "force", "moduleResolution": "bundler", "allowImportingTsExtensions": true, "verbatimModuleSyntax": true, "noEmit": true, "strict": true, "skipLibCheck": true, "types": ["bun-types"] } }
Type checking (separate step):
bunx tsc --noEmit
- Configuration (bunfig.toml)
Runtime
preload = ["./setup.ts"] smol = true # Reduced memory mode
JSX
[jsx] runtime = "automatic" importSource = "react"
Package installation
[install] optional = false lockfile.save = true
[install.scopes] "@myorg" = { url = "https://npm.myorg.com", token = "$NPM_TOKEN" }
Test runner
[test] preload = ["./test-setup.ts"] coverage = true coverageThreshold = { lines = 0.8, functions = 0.8 }
- CLI Flags Reference
Execution:
-
--watch
-
Auto-restart on file changes
-
--hot
-
Hot module replacement
-
--smol
-
Reduced memory mode
-
--inspect / --inspect-brk
-
Debugger
Module Resolution:
-
--preload / -r
-
Preload modules
-
--install=auto|fallback|force
-
Auto-install behavior
Transpilation:
-
--define / -d
-
Compile-time constants
-
--drop=console
-
Remove function calls
-
--loader
-
Custom file loaders
Environment:
-
--env-file
-
Load specific .env files
-
--cwd
-
Set working directory
-
--bun / -b
-
Force Bun runtime
Node.js Migration Guidance
Quick Migration Steps
-
Install Bun: curl -fsSL https://bun.sh/install | bash
-
Replace package manager:
Delete node_modules and lockfile
rm -rf node_modules package-lock.json yarn.lock pnpm-lock.yaml bun install
- Update scripts in package.json:
{ "scripts": { "dev": "bun run --watch src/index.ts", "test": "bun test", "build": "bun build src/index.ts --outdir dist" } }
- Update TypeScript types:
bun add -d @types/bun
Or in tsconfig.json: "types": ["bun-types"]
API Compatibility
Fully Compatible:
-
node:assert , node:buffer , node:events , node:path , node:url
-
node:fs (92%), node:http , node:https , node:stream , node:zlib
-
node:crypto , node:net , node:dns , node:os
Partially Compatible:
-
node:child_process
-
Missing proc.gid , proc.uid ; IPC limited to JSON
-
node:cluster
-
Linux-only SO_REUSEPORT for load balancing
-
node:http2
-
95% compatible; missing pushStream
-
node:worker_threads
-
Missing stdin , stdout , stderr options
-
node:async_hooks
-
AsyncLocalStorage works; v8 promise hooks missing
Not Implemented:
- node:inspector , node:repl , node:trace_events
Common Migration Gotchas
-
Native Modules: Packages using node-gyp (bcrypt, sharp) may fail
-
Solution: Use pure JS alternatives (bcryptjs instead of bcrypt )
-
Lifecycle Scripts: Bun blocks postinstall by default (security)
-
Solution: bun pm trust <package> or add to trustedDependencies
-
Module System: Some packages relying on Node.js internals may fail
-
Module._nodeModulePaths doesn't exist in Bun
-
File System Differences: Heavy concurrent file reads can cause memory issues
-
Solution: Use graceful-fs wrapper
-
TypeScript Entry Points: Update "main" field for Bun:
{ "main": "src/index.ts" // Not "build/index.js" }
Gradual Migration Strategy
Phase 1: Package manager only (bun install ) Phase 2: Development tooling (bun run , bun test ) Phase 3: Selective runtime migration (shadow deploy) Phase 4: Full production migration
Best Practices
Performance Optimization
// Use Bun's native APIs for I/O const file = Bun.file("large.txt"); // Zero-copy const content = await file.text(); await Bun.write("output.txt", processedData);
// Use Promise.all for concurrent operations const [users, posts] = await Promise.all([ db.query("SELECT * FROM users"), db.query("SELECT * FROM posts") ]);
// Use Bun.serve() static routes Bun.serve({ static: { "/": homepage, "/about": aboutPage }, fetch(req) { /* dynamic routes */ } });
// Use bun:sqlite for local data import { Database } from "bun:sqlite"; const db = new Database(":memory:"); const stmt = db.prepare("SELECT * FROM users WHERE id = ?");
Error Handling
// HTTP server error handling
Bun.serve({
fetch(req) {
try {
return handleRequest(req);
} catch (error) {
console.error(error);
return new Response("Internal Error", { status: 500 });
}
},
error(error) {
return new Response(Error: ${error.message}, { status: 500 });
}
});
// Process-level error handling process.on("uncaughtException", (error) => { console.error("Uncaught:", error); process.exit(1); });
Security Best Practices
-
Lifecycle Scripts: Keep trustedDependencies minimal
-
Environment Variables: Use .env.local for secrets (not committed)
-
Input Validation: Sanitize all user inputs
-
Dependencies: Run bun audit regularly
-
Production: Use --production flag to skip devDependencies
Project Structure
my-bun-project/ ├── src/ │ ├── index.ts # Entry point │ ├── server.ts # HTTP server │ └── lib/ # Utilities ├── test/ │ └── *.test.ts # Test files ├── bunfig.toml # Bun configuration ├── tsconfig.json # TypeScript config ├── package.json └── .env.local # Local secrets (gitignored)
Debugging
Web Debugger:
bun --inspect server.ts # Start debugger bun --inspect-brk server.ts # Break at first line bun --inspect-wait server.ts # Wait for connection
Open https://debug.bun.sh or use VSCode Bun extension.
Verbose Fetch Logging:
BUN_CONFIG_VERBOSE_FETCH=curl bun run server.ts
Examples
HTTP Server with WebSocket
Bun.serve({
port: 3000,
fetch(req, server) {
if (server.upgrade(req)) return;
return new Response("Hello Bun!");
},
websocket: {
open(ws) { console.log("Connected"); },
message(ws, message) { ws.send(Echo: ${message}); },
close(ws) { console.log("Disconnected"); }
}
});
File Server
Bun.serve({
async fetch(req) {
const path = new URL(req.url).pathname;
const file = Bun.file(./public${path});
if (await file.exists()) {
return new Response(file);
}
return new Response("Not Found", { status: 404 });
}
});
Database with SQLite
import { Database } from "bun:sqlite";
const db = new Database("app.db");
db.run(CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, email TEXT UNIQUE ));
const insert = db.prepare("INSERT INTO users (name, email) VALUES (?, ?)"); const getAll = db.prepare("SELECT * FROM users");
insert.run("Alice", "alice@example.com"); const users = getAll.all();
When This Skill Activates
This skill automatically activates when:
-
Working with .ts , .tsx , .js , .jsx files in a Bun project
-
Creating or modifying bunfig.toml or bun.lock
-
Using bun:test , bun:sqlite , bun:ffi imports
-
Discussing Bun APIs (Bun.serve, Bun.file, Bun.build)
-
Migrating from Node.js to Bun
-
Writing or debugging Bun tests
-
Configuring the Bun bundler