Bun - The Fast JavaScript Runtime
Build and run JavaScript/TypeScript applications with Bun's all-in-one toolkit.
Quick Start
bash
# Install Bun (macOS, Linux, WSL) curl -fsSL https://bun.sh/install | bash # Windows powershell -c "irm bun.sh/install.ps1 | iex" # Create new project bun init # Run TypeScript directly (no build step!) bun run index.ts # Install packages (faster than npm) bun install # Run scripts bun run dev
Package Management
bash
# Install dependencies bun install # Install all from package.json bun add express # Add dependency bun add -d typescript # Add dev dependency bun add -g serve # Add global package # Remove packages bun remove express # Update packages bun update # Run package binaries bunx prisma generate # Like npx but faster bunx create-next-app # Lockfile bun install --frozen-lockfile # CI mode
bun.lockb vs package-lock.json
bash
# Bun uses binary lockfile (bun.lockb) - much faster # To generate yarn.lock for compatibility: bun install --yarn # Import from other lockfiles bun install # Auto-detects package-lock.json, yarn.lock
Bun Runtime
Run Files
bash
# Run any file bun run index.ts # TypeScript bun run index.js # JavaScript bun run index.jsx # JSX # Watch mode bun --watch run index.ts # Hot reload bun --hot run server.ts
Built-in APIs
typescript
// File I/O (super fast)
const file = Bun.file('data.json');
const content = await file.text();
const json = await file.json();
const bytes = await file.arrayBuffer();
// Write files
await Bun.write('output.txt', 'Hello, Bun!');
await Bun.write('data.json', JSON.stringify({ key: 'value' }));
await Bun.write('image.png', await fetch('https://example.com/img.png'));
// File metadata
const file = Bun.file('data.json');
console.log(file.size); // bytes
console.log(file.type); // MIME type
console.log(file.lastModified);
// Glob files
const glob = new Bun.Glob('**/*.ts');
for await (const file of glob.scan('.')) {
console.log(file);
}
HTTP Server
typescript
// Simple server
const server = Bun.serve({
port: 3000,
fetch(req) {
const url = new URL(req.url);
if (url.pathname === '/') {
return new Response('Hello, Bun!');
}
if (url.pathname === '/json') {
return Response.json({ message: 'Hello!' });
}
return new Response('Not Found', { status: 404 });
},
});
console.log(`Server running at http://localhost:${server.port}`);
Advanced Server
typescript
Bun.serve({
port: 3000,
// Main request handler
async fetch(req, server) {
const url = new URL(req.url);
// WebSocket upgrade
if (url.pathname === '/ws') {
const upgraded = server.upgrade(req, {
data: { userId: '123' }, // Attach data to socket
});
if (upgraded) return undefined;
}
// Static files
if (url.pathname.startsWith('/static/')) {
const filePath = `./public${url.pathname}`;
const file = Bun.file(filePath);
if (await file.exists()) {
return new Response(file);
}
}
// JSON API
if (url.pathname === '/api/data' && req.method === 'POST') {
const body = await req.json();
return Response.json({ received: body });
}
return new Response('Not Found', { status: 404 });
},
// WebSocket handlers
websocket: {
open(ws) {
console.log('Client connected:', ws.data.userId);
ws.subscribe('chat'); // Pub/sub
},
message(ws, message) {
// Broadcast to all subscribers
ws.publish('chat', message);
},
close(ws) {
console.log('Client disconnected');
},
},
// Error handling
error(error) {
return new Response(`Error: ${error.message}`, { status: 500 });
},
});
WebSocket Client
typescript
const ws = new WebSocket('ws://localhost:3000/ws');
ws.onopen = () => {
ws.send('Hello, server!');
};
ws.onmessage = (event) => {
console.log('Received:', event.data);
};
Bun APIs
SQLite (Built-in)
typescript
import { Database } from 'bun:sqlite';
const db = new Database('mydb.sqlite');
// Create table
db.run(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE
)
`);
// Insert
const insert = db.prepare('INSERT INTO users (name, email) VALUES (?, ?)');
insert.run('Alice', 'alice@example.com');
// Query
const query = db.prepare('SELECT * FROM users WHERE id = ?');
const user = query.get(1);
// All results
const allUsers = db.prepare('SELECT * FROM users').all();
// Transaction
const insertMany = db.transaction((users) => {
for (const user of users) {
insert.run(user.name, user.email);
}
});
insertMany([
{ name: 'Bob', email: 'bob@example.com' },
{ name: 'Charlie', email: 'charlie@example.com' },
]);
Password Hashing (Built-in)
typescript
// Hash password
const hash = await Bun.password.hash('mypassword', {
algorithm: 'argon2id', // or 'bcrypt'
memoryCost: 65536, // 64 MB
timeCost: 2,
});
// Verify password
const isValid = await Bun.password.verify('mypassword', hash);
Spawn Processes
typescript
// Spawn process
const proc = Bun.spawn(['ls', '-la'], {
cwd: '/home/user',
env: { ...process.env, MY_VAR: 'value' },
stdout: 'pipe',
});
const output = await new Response(proc.stdout).text();
console.log(output);
// Spawn sync
const result = Bun.spawnSync(['echo', 'hello']);
console.log(result.stdout.toString());
// Shell command
const { stdout } = Bun.spawn({
cmd: ['sh', '-c', 'echo $HOME'],
stdout: 'pipe',
});
Hashing & Crypto
typescript
// Hash strings
const hash = Bun.hash('hello world'); // Wyhash (fast)
// Crypto hashes
const sha256 = new Bun.CryptoHasher('sha256');
sha256.update('data');
const digest = sha256.digest('hex');
// One-liner
const md5 = Bun.CryptoHasher.hash('md5', 'data', 'hex');
// HMAC
const hmac = Bun.CryptoHasher.hmac('sha256', 'secret-key', 'data', 'hex');
Bundler
bash
# Bundle for browser bun build ./src/index.ts --outdir ./dist # Bundle options bun build ./src/index.ts \ --outdir ./dist \ --minify \ --sourcemap \ --target browser \ --splitting \ --entry-naming '[dir]/[name]-[hash].[ext]'
Build API
typescript
const result = await Bun.build({
entrypoints: ['./src/index.ts'],
outdir: './dist',
minify: true,
sourcemap: 'external',
target: 'browser', // 'bun' | 'node' | 'browser'
splitting: true,
naming: {
entry: '[dir]/[name]-[hash].[ext]',
chunk: '[name]-[hash].[ext]',
asset: '[name]-[hash].[ext]',
},
external: ['react', 'react-dom'],
define: {
'process.env.NODE_ENV': JSON.stringify('production'),
},
loader: {
'.png': 'file',
'.svg': 'text',
},
});
if (!result.success) {
console.error('Build failed:', result.logs);
}
Testing
typescript
// test.ts
import { describe, test, expect, beforeAll, afterAll, mock } from 'bun:test';
describe('Math operations', () => {
test('addition', () => {
expect(1 + 1).toBe(2);
});
test('array contains', () => {
expect([1, 2, 3]).toContain(2);
});
test('object matching', () => {
expect({ name: 'Alice', age: 30 }).toMatchObject({ name: 'Alice' });
});
test('async test', async () => {
const result = await Promise.resolve(42);
expect(result).toBe(42);
});
test('throws error', () => {
expect(() => {
throw new Error('fail');
}).toThrow('fail');
});
});
// Mocking
const mockFn = mock(() => 'mocked');
mockFn();
expect(mockFn).toHaveBeenCalled();
// Mock modules
mock.module('./database', () => ({
query: mock(() => [{ id: 1 }]),
}));
bash
# Run tests bun test # Watch mode bun test --watch # Specific file bun test user.test.ts # Coverage bun test --coverage
Node.js Compatibility
typescript
// Most Node.js APIs work out of the box
import fs from 'fs';
import path from 'path';
import { createServer } from 'http';
import express from 'express';
// Bun implements Node.js APIs
const data = fs.readFileSync('file.txt', 'utf-8');
const fullPath = path.join(__dirname, 'file.txt');
// Express works!
const app = express();
app.get('/', (req, res) => res.send('Hello!'));
app.listen(3000);
Node.js vs Bun APIs
typescript
// Node.js way
import { readFile } from 'fs/promises';
const content = await readFile('file.txt', 'utf-8');
// Bun way (faster)
const content = await Bun.file('file.txt').text();
// Node.js crypto
import crypto from 'crypto';
const hash = crypto.createHash('sha256').update('data').digest('hex');
// Bun way (faster)
const hash = Bun.CryptoHasher.hash('sha256', 'data', 'hex');
Environment Variables
typescript
// .env file support (built-in, no dotenv needed!) // .env // DATABASE_URL=postgres://localhost/db // API_KEY=secret // Access env vars const dbUrl = Bun.env.DATABASE_URL; const apiKey = process.env.API_KEY; // Also works // bunfig.toml for Bun config // [run] // preload = ["./setup.ts"]
HTTP Client
typescript
// Fetch (optimized in Bun)
const response = await fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token',
},
body: JSON.stringify({ key: 'value' }),
});
const data = await response.json();
// Streaming response
const response = await fetch('https://api.example.com/stream');
const reader = response.body?.getReader();
while (true) {
const { done, value } = await reader!.read();
if (done) break;
console.log(new TextDecoder().decode(value));
}
Project Structure
code
my-bun-project/ ├── src/ │ ├── index.ts # Entry point │ ├── routes/ │ │ └── api.ts │ └── lib/ │ └── database.ts ├── tests/ │ └── index.test.ts ├── public/ │ └── static files ├── package.json ├── bunfig.toml # Bun config (optional) ├── tsconfig.json └── .env
bunfig.toml
toml
[install] # Use exact versions by default exact = true # Registry registry = "https://registry.npmjs.org" [run] # Scripts to run before `bun run` preload = ["./instrumentation.ts"] [test] # Test configuration coverage = true coverageDir = "coverage" [bundle] # Default bundle config minify = true sourcemap = "external"
Performance Comparison
| Operation | Node.js | Bun | Speedup |
|---|---|---|---|
| Start time | ~40ms | ~7ms | 5.7x |
| Package install | ~10s | ~1s | 10x |
| File read | baseline | faster | 10x |
| HTTP server | baseline | faster | 4x |
| SQLite | external | built-in | 3x |
| TypeScript | compile needed | native | ∞ |
Resources
- •Bun Docs: https://bun.sh/docs
- •Bun API Reference: https://bun.sh/docs/api
- •Bun Discord: https://bun.sh/discord
- •GitHub: https://github.com/oven-sh/bun