--- name: bun-dev description: This skill should be used when working with Bun runtime, bun:sqlite, Bun.serve, bun:test, or when "Bun", "bun:test", or Bun-specific patterns are mentioned. metadata: version: "1.0.0" --- # Bun Development Bun runtime → native APIs → zero-dependency patterns. - Bun runtime development - SQLite database with bun:sqlite - HTTP server with Bun.serve - Testing with bun:test - File operations with Bun.file/Bun.write - Shell operations with $ template - Password hashing with Bun.password - Environment variable handling - Building and bundling NOT for: Node.js-only patterns, cross-runtime libraries, non-Bun projects **Package management**: ```bash bun install # Install deps bun add zod # Add package bun remove zod # Remove package bun update # Update all ``` **Script execution**: ```bash bun run dev # Run package.json script bun run src/index.ts # Execute TypeScript directly bun --watch index.ts # Watch mode ``` **Testing**: ```bash bun test # All tests bun test src/ # Directory bun test --watch # Watch mode bun test --coverage # With coverage ``` **Building**: ```bash bun build ./index.ts --outfile dist/bundle.js bun build ./index.ts --compile --outfile myapp # Standalone executable ``` ## File Operations ```typescript // Read file (lazy, efficient) const file = Bun.file('./data.json'); if (!(await file.exists())) throw new Error('File not found'); // Read formats const text = await file.text(); const json = await file.json(); const buffer = await file.arrayBuffer(); const stream = file.stream(); // Large files // Metadata console.log(file.size, file.type); // Write await Bun.write('./output.txt', 'content'); await Bun.write('./data.json', JSON.stringify(data)); await Bun.write('./blob.txt', new Blob(['data'])); ``` ## SQLite (bun:sqlite) ```typescript import { Database } from 'bun:sqlite'; const db = new Database('app.db', { create: true, readwrite: true, strict: true }); // Create tables db.run(` CREATE TABLE IF NOT EXISTS users ( id TEXT PRIMARY KEY, email TEXT UNIQUE NOT NULL, name TEXT NOT NULL, created_at TEXT DEFAULT CURRENT_TIMESTAMP ) `); // Prepared statements (always use these) const getUser = db.prepare('SELECT * FROM users WHERE id = ?'); const createUser = db.prepare('INSERT INTO users (id, email, name) VALUES (?, ?, ?) RETURNING *'); // Execution const user = getUser.get('user-123'); // Single row const all = db.prepare('SELECT * FROM users').all(); // All rows db.prepare('DELETE FROM users WHERE id = ?').run('id'); // No return // Named parameters const stmt = db.prepare('SELECT * FROM users WHERE email = $email'); stmt.get({ $email: 'alice@example.com' }); // Transactions (atomic, auto-rollback on error) const transfer = db.transaction((fromId: string, toId: string, amount: number) => { db.run('UPDATE accounts SET balance = balance - ? WHERE id = ?', [amount, fromId]); db.run('UPDATE accounts SET balance = balance + ? WHERE id = ?', [amount, toId]); }); transfer('alice', 'bob', 100); db.close(); // When done ``` See [sqlite-patterns.md](references/sqlite-patterns.md) for migrations, pooling, repository pattern. ## Password Hashing ```typescript // Hash (argon2id recommended) const hash = await Bun.password.hash('password123', { algorithm: 'argon2id', memoryCost: 65536, // 64 MB timeCost: 3 }); // Or bcrypt const bcryptHash = await Bun.password.hash('password123', { algorithm: 'bcrypt', cost: 12 }); // Verify const isValid = await Bun.password.verify('password123', hash); if (!isValid) throw new Error('Invalid password'); ``` **Auth flow example**: ```typescript app.post('/auth/register', zValidator('json', RegisterSchema), async (c) => { const { email, password } = c.req.valid('json'); const db = c.get('db'); if (db.prepare('SELECT id FROM users WHERE email = ?').get(email)) { throw new HTTPException(409, { message: 'Email already registered' }); } const hashedPassword = await Bun.password.hash(password, { algorithm: 'argon2id' }); const user = db.prepare(` INSERT INTO users (id, email, password) VALUES (?, ?, ?) RETURNING id, email `).get(crypto.randomUUID(), email, hashedPassword); return c.json({ user }, 201); }); ``` ## HTTP Server ```typescript Bun.serve({ port: 3000, fetch(req) { const url = new URL(req.url); if (url.pathname === '/') return new Response('Hello'); if (url.pathname === '/json') return Response.json({ ok: true }); return new Response('Not found', { status: 404 }); }, error(err) { return new Response(`Error: ${err.message}`, { status: 500 }); } }); ``` **With Hono** (recommended for APIs): ```typescript import { Hono } from 'hono'; const app = new Hono() .get('/', (c) => c.text('Hello')) .get('/json', (c) => c.json({ ok: true })); Bun.serve({ port: 3000, fetch: app.fetch }); ``` See [server-patterns.md](references/server-patterns.md) for routing, middleware, file serving, streaming. ## WebSocket ```typescript import type { ServerWebSocket } from 'bun'; type WsData = { userId: string }; Bun.serve({ port: 3000, fetch(req, server) { const url = new URL(req.url); if (url.pathname === '/ws') { const userId = url.searchParams.get('userId') || 'anon'; return server.upgrade(req, { data: { userId } }) ? undefined : new Response('Upgrade failed', { status: 400 }); } return new Response('Hello'); }, websocket: { open(ws: ServerWebSocket) { ws.subscribe('chat'); ws.send(JSON.stringify({ type: 'connected' })); }, message(ws: ServerWebSocket, msg: string | Buffer) { ws.publish('chat', msg); }, close(ws: ServerWebSocket) { ws.unsubscribe('chat'); } } }); ``` See [server-patterns.md](references/server-patterns.md) for client tracking, rooms, reconnection. ## Shell Operations ```typescript import { $ } from 'bun'; // Run commands const result = await $`ls -la`; console.log(result.text()); // Variables (auto-escaped) const dir = './src'; await $`find ${dir} -name "*.ts"`; // Check exit code const { exitCode } = await $`npm test`.nothrow(); if (exitCode !== 0) console.error('Tests failed'); // Spawn process const proc = Bun.spawn(['ls', '-la']); await proc.exited; // Capture output const proc2 = Bun.spawn(['echo', 'Hello'], { stdout: 'pipe' }); const output = await new Response(proc2.stdout).text(); ``` ## Testing (bun:test) ```typescript import { describe, test, expect, beforeEach, afterEach } from 'bun:test'; describe('feature', () => { let db: Database; beforeEach(() => { db = new Database(':memory:'); }); afterEach(() => { db.close(); }); test('behavior', () => { expect(result).toBe(expected); expect(arr).toContain(item); expect(fn).toThrow(); expect(obj).toEqual({ foo: 'bar' }); }); test('async', async () => { const result = await asyncFn(); expect(result).toBeDefined(); }); test.todo('pending feature'); test.skip('temporarily disabled'); }); ``` ```bash bun test # All tests bun test src/api.test.ts # Specific file bun test --watch # Watch mode bun test --coverage # With coverage ``` See [testing.md](references/testing.md) for assertions, mocking, snapshots, best practices. ## Environment Variables ```typescript // Access console.log(Bun.env.NODE_ENV); console.log(Bun.env.DATABASE_URL); // Zod validation import { z } from 'zod'; const EnvSchema = z.object({ NODE_ENV: z.enum(['development', 'production', 'test']).default('development'), DATABASE_URL: z.string(), PORT: z.coerce.number().int().positive().default(3000), API_KEY: z.string().min(32) }); export const env = EnvSchema.parse(Bun.env); ``` Bun auto-loads `.env`, `.env.local`, `.env.production`. ## Performance Utilities ```typescript // High-resolution timing const start = Bun.nanoseconds(); await doWork(); console.log(`Took ${(Bun.nanoseconds() - start) / 1_000_000}ms`); // Hashing const hash = Bun.hash(data); const crc32 = Bun.hash.crc32(data); const sha256 = Bun.CryptoHasher.hash('sha256', data); // Sleep await Bun.sleep(1000); // Memory const { rss, heapUsed } = process.memoryUsage(); console.log('RSS:', rss / 1024 / 1024, 'MB'); ``` ## Building & Bundling ```bash # Production bundle bun build ./index.ts --outfile dist/bundle.js --minify --sourcemap # External deps bun build ./index.ts --outfile dist/bundle.js --external hono --external zod # Standalone executable bun build ./index.ts --compile --outfile myapp # Cross-compile bun build ./index.ts --compile --target=bun-linux-x64 --outfile myapp-linux bun build ./index.ts --compile --target=bun-darwin-arm64 --outfile myapp-macos bun build ./index.ts --compile --target=bun-windows-x64 --outfile myapp.exe ``` ALWAYS: - Use Bun APIs when available (faster, native) - Prepared statements for database queries - Transactions for multi-statement operations - argon2id for password hashing - Validate environment variables at startup - Close database connections when done NEVER: - String interpolation in SQL (use parameters) - Plaintext passwords - Ignore async disposal cleanup - Deprecated Node.js APIs when Bun native exists PREFER: - Bun.file over fs.readFile - Bun.write over fs.writeFile - bun:sqlite over external SQLite libraries - Bun.password over bcrypt/argon2 packages - $ shell template over child_process - [sqlite-patterns.md](references/sqlite-patterns.md) — migrations, pooling, repository, FTS - [server-patterns.md](references/server-patterns.md) — HTTP, WebSocket, streaming, compression - [testing.md](references/testing.md) — assertions, mocking, snapshots, best practices **Examples:** - [database-crud.md](examples/database-crud.md) — SQLite CRUD patterns - [file-uploads.md](examples/file-uploads.md) — streaming file handling