Bun - The Fast JavaScript Runtime
Build and run JavaScript/TypeScript applications with Bun's all-in-one toolkit.
Quick Start
# 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
# 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
# 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
# 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
// 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
// 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
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
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)
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)
// 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
// 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
// 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
# 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
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
// 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 }]),
}));
# Run tests
bun test
# Watch mode
bun test --watch
# Specific file
bun test user.test.ts
# Coverage
bun test --coverage
Node.js Compatibility
// 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
// 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
// .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
// 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
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
[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