bun-expert
Comprehensive Bun runtime expertise covering all major features. Use when working with Bun projects, migrating from Node.js, or leveraging Bun-specific APIs. Activates for: Bun.serve, Bun.file, bun:test, bun:sqlite, bun install, bun build, bunfig.toml, TypeScript in Bun, package manager operations, bundler configuration, and Node.js compatibility questions.
Installation
Follow the repository instructions for this skill and your agent. Review the files and dependencies before adding it to your workflow.
View source ↗GuidesSkill instructions
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
1. 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
2. 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.
3. 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--onlyflag) -
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
4. 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"
};
});
}
};
5. 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
6. 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 }
7. 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- Missingproc.gid,proc.uid; IPC limited to JSON -
node:cluster- Linux-only SO_REUSEPORT for load balancing -
node:http2- 95% compatible; missingpushStream -
node:worker_threads- Missingstdin,stdout,stderroptions -
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 (
bcryptjsinstead ofbcrypt)
- Solution: Use pure JS alternatives (
-
Lifecycle Scripts: Bun blocks postinstall by default (security)
- Solution:
bun pm trust <package>or add totrustedDependencies
- Solution:
-
Module System: Some packages relying on Node.js internals may fail
-
Module._nodeModulePathsdoesn't exist in Bun
-
-
File System Differences: Heavy concurrent file reads can cause memory issues
- Solution: Use
graceful-fswrapper
- Solution: Use
-
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
trustedDependenciesminimal -
Environment Variables: Use
.env.localfor secrets (not committed) - Input Validation: Sanitize all user inputs
-
Dependencies: Run
bun auditregularly -
Production: Use
--productionflag 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,.jsxfiles in a Bun project - Creating or modifying
bunfig.tomlorbun.lock - Using
bun:test,bun:sqlite,bun:ffiimports - Discussing Bun APIs (Bun.serve, Bun.file, Bun.build)
- Migrating from Node.js to Bun
- Writing or debugging Bun tests
- Configuring the Bun bundler
Examples
Read the usage examples in the source repository.
View source ↗Source history
- Indexed on
- 2026-03-11
- Index updated
- 2026-03-11
- Branch
- main
Index and sync dates describe this directory. View the source repository for code history.
GitHub ↗chat Comments (0)
Sign in to join the discussion and leave a comment.
No comments yet. Be the first to share your thoughts!