Bun Runtime Migration: Porting High-Traffic Node.js APIs with Native APIs and SQLite
Bun Runtime Migration: Porting High-Traffic Node.js APIs with Native APIs and SQLite
Bun delivers 4× HTTP throughput and significant SQLite performance gains over Node.js. This guide covers migrating high-traffic APIs using Bun's native APIs for maximum performance.
Core Runtime Migration
Replace Express/Fastify with Bun.serve for native fetch-based HTTP handling.
Node.js Express pattern:
import express from 'express';
const app = express();
app.use(express.json());
app.get('/users', (req, res) => {
res.json({ users: [] });
});
app.listen(3000);
Bun native equivalent:
Bun.serve({
port: 3000,
async fetch(request) {
const url = new URL(request.url);
if (url.pathname === '/users') {
return Response.json({ users: [] });
}
return new Response('Not Found', { status: 404 });
},
});
Performance gain: Bun 1.1.20 handles ~52,000 requests/second vs Node.js 20.15.0's 13,000 in HTTP benchmarks (tested on 8-core Linux with autocannon, 100 concurrent connections, simple JSON response).
Native API Integration
Replace external dependencies with Bun's built-in globals.
Password Hashing
Replace bcrypt with Bun.password:
// Node.js
import bcrypt from 'bcrypt';
const hash = await bcrypt.hash(password, 10);
// Bun
try {
const hash = await Bun.password.hash(password);
const match = await Bun.password.verify(password, hash);
} catch (error) {
console.error('Password hashing failed:', error);
}
File I/O
Replace fs with Bun.file:
// Node.js
import { readFile } from 'fs/promises';
const content = await readFile('./data.json', 'utf8');
// Bun
try {
const file = Bun.file('./data.json');
const content = await file.text();
} catch (error) {
console.error('File read failed:', error);
}
Database Layer: bun:sqlite
Use bun:sqlite for synchronous, high-performance SQLite operations. This is a built-in module requiring no installation.
Basic usage with WAL mode:
import { Database } from 'bun:sqlite';
const db = new Database('app.db');
// Enable WAL mode for concurrent reads/writes
db.exec('PRAGMA journal_mode = WAL;');
db.exec('PRAGMA synchronous = NORMAL;');
// Create table
db.exec(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
email TEXT UNIQUE,
name TEXT
)
`);
// Insert with prepared statement
const insert = db.prepare('INSERT INTO users (email, name) VALUES (?, ?)');
insert.run('user@example.com', 'John Doe');
// Query data
const users = db.prepare('SELECT * FROM users WHERE id = ?').all(1);
High-traffic considerations: bun:sqlite is synchronous and runs on a single connection. WAL mode enables concurrent reads while serializing writes. For write-heavy workloads, consider batching operations within transactions. Unlike Node.js async drivers, there's no connection pooling overhead.
High-traffic transaction handling with db.transaction():
// Batch insert with transaction for better performance
const insert = db.prepare('INSERT INTO users (email, name) VALUES (?, ?)');
// Define users data
const users = [
{ email: 'alice@example.com', name: 'Alice' },
{ email: 'bob@example.com', name: 'Bob' },
{ email: 'charlie@example.com', name: 'Charlie' },
];
// Use db.transaction() for automatic commit/rollback
const batchInsert = db.transaction((users) => {
for (const user of users) {
insert.run(user.email, user.name);
}
});
try {
batchInsert(users);
console.log('Batch insert completed');
} catch (error) {
console.error('Batch insert failed:', error);
}
The db.transaction() method wraps operations in a transaction that automatically commits on success or rolls back on error.
Performance comparison (Bun 1.1.20 vs Node.js 20.15.0 with better-sqlite3, complex transactions with indexes and WAL mode):
- Bun: 81,370 queries/second
- Node.js (better-sqlite3): 21,290 queries/second
- Speedup: 3.8× faster
Simple CRUD operations typically exceed 100,000 queries/second on both runtimes.
Complete API Migration Example
Before (Node.js + Express + better-sqlite3):
import express from 'express';
import Database from 'better-sqlite3';
import bcrypt from 'bcrypt';
const app = express();
const db = new Database('app.db');
const PORT = 3000;
app.use(express.json());
app.post('/register', async (req, res) => {
try {
const { email, password, name } = req.body;
if (!email || !password || !name) {
return res.status(400).json({ error: 'Missing required fields' });
}
const hash = await bcrypt.hash(password, 10);
const stmt = db.prepare('INSERT INTO users (email, password, name) VALUES (?, ?, ?)');
stmt.run(email, hash, name);
res.json({ success: true });
} catch (error) {
console.error('Registration failed:', error);
res.status(500).json({ error: 'Registration failed' });
}
});
app.listen(PORT);
After (Bun native with TypeScript):
import { Database } from 'bun:sqlite';
interface User {
id: number;
email: string;
password: string;
name: string;
}
const db = new Database('app.db');
db.exec('PRAGMA journal_mode = WAL;');
db.exec('PRAGMA synchronous = NORMAL;');
const server = Bun.serve({
port: 3000,
maxRequestBodySize: 1024 * 1024, // 1MB limit for high-traffic protection
async fetch(request) {
const url = new URL(request.url);
if (url.pathname === '/register' && request.method === 'POST') {
try {
const body = await request.json() as Partial<User>;
const { email, password, name } = body;
if (!email || !password || !name) {
return Response.json({ error: 'Missing required fields' }, { status: 400 });
}
const hash = await Bun.password.hash(password);
const stmt = db.prepare('INSERT INTO users (email, password, name) VALUES (?, ?, ?)');
stmt.run(email, hash, name);
return Response.json({ success: true });
} catch (error) {
console.error('Registration failed:', error);
return Response.json({ error: 'Registration failed' }, { status: 500 });
}
}
return new Response('Not Found', { status: 404 });
},
});
// Graceful shutdown
process.on('SIGINT', () => {
console.log('Shutting down gracefully...');
db.close();
server.stop();
Bun.exit(0);
});
process.on('SIGTERM', () => {
console.log('Shutting down gracefully...');
db.close();
server.stop();
Bun.exit(0);
});
TypeScript benefits: Bun natively supports TypeScript without configuration. The .ts extension provides type safety for request bodies, database results, and API responses. Use bun build --compile ./src/index.ts to compile directly from TypeScript to a standalone binary.
Development Workflow
Hot reloading for development:
bun --hot src/index.ts
The --hot flag automatically restarts the server on file changes, enabling rapid iteration during migration.
npm Package Compatibility
Most npm packages work unchanged in Bun. However, some packages may require attention:
- Native modules: Packages with native bindings (e.g.,
sharp,bcrypt) may need Bun-specific versions or replacement with Bun's built-in alternatives (Bun.passwordreplaces bcrypt). - Node.js-specific APIs: Packages using
process.nextTick,setImmediate, or internal Node APIs usually work but should be tested. - Testing frameworks: Jest migrations should use
bun testfor faster execution with a compatible API.
Run bun install to install dependencies. Bun's package manager is significantly faster than npm, typically completing in milliseconds.
Production Deployment
Environment variables:
const PORT = Bun.env.PORT || 3000;
const DB_PATH = Bun.env.DB_PATH || './app.db';
Binary compilation:
bun build --compile ./src/index.ts --outfile myapp
Optimized Dockerfile:
FROM oven/bun:1.1.20 AS builder
WORKDIR /app
COPY package.json bun.lockb ./
COPY src ./src
RUN bun build --compile ./src/index.ts --outfile /app/myapp
FROM alpine:latest
COPY --from=builder /app/myapp /app/myapp
EXPOSE 3000
CMD ["/app/myapp"]
Migration Checklist
- Test compatibility: Migrate Jest tests to
bun test(similar API, faster execution) - Replace HTTP layer: Convert Express routes to
Bun.servehandlers - Migrate dependencies: Replace bcrypt →
Bun.password, fs →Bun.file - Database migration: Switch to
bun:sqlitewith WAL mode enabled - Add transaction handling: Use
db.transaction()for batch operations - Set body limits: Configure
maxRequestBodySizefor high-traffic protection - Implement graceful shutdown: Handle SIGINT/SIGTERM with
Bun.exit() - Performance validation: Benchmark critical endpoints
- Shadow deployment: Mirror traffic before full cutover
Expected gains: 3-4× request throughput, 2× faster cold starts, 3.8× database query performance.
MatterAI builds frontier AI infrastructure for engineering teams — from inference-optimized models to autonomous coding agents and agentic code reviews.
Explore what we're building:
- Orbital IDE — Autonomous AI coding agent with background agents and deep codebase memory
- AI Code Reviews — Agentic pre-commit reviews across GitHub, GitLab, and Bitbucket
- Axon Models — Frontier-grade reasoning models at 70% lower inference cost
Share this Guide:
More Guides
Local LLMs in Your IDE: Connecting Ollama to Coding Agents and Autocomplete
Wire local models into VS Code, JetBrains, Cline, Continue, and Aider via the OpenAI-compatible API. Covers model routing, context budgets, tool calling with small models, and when a local model is the right choice for the job.
15 min readBuilding a Self-Hosted AI Stack: Ollama, Open WebUI, and Local RAG
Stand up a fully self-hosted AI stack on a single machine: Ollama for inference, Open WebUI as the chat interface, local embeddings for RAG, and a reverse proxy for secure access. No cloud dependency, no data leaving your network.
17 min readTop 5 Open-Source Coding Models to Run on Your Mac (2026)
The best local coding models for Apple Silicon in 2026, ranked by quality per gigabyte of unified memory. Covers qwen3-coder, devstral, gpt-oss, and more with real pull tags, sizes, and context windows.
14 min readRunning LLMs Locally: GGUF, Quantization, and Memory Planning
Learn the GGUF format, the quantization ladder from Q2 to FP16, and the exact memory math for running models on Apple Silicon and NVIDIA GPUs. Includes Ollama and llama.cpp tuning for KV cache and context.
15 min readOllama vs vLLM vs llama.cpp: Choosing the Right Local LLM Runtime
Compare the three dominant local LLM runtimes on architecture, throughput, hardware, and deployment context. Includes benchmark data, a decision framework, and a migration path from Ollama to vLLM.
16 min readContinue Reading
Local LLMs in Your IDE: Connecting Ollama to Coding Agents and Autocomplete
Wire local models into VS Code, JetBrains, Cline, Continue, and Aider via the OpenAI-compatible API. Covers model routing, context budgets, tool calling with small models, and when a local model is the right choice for the job.
15 min readBuilding a Self-Hosted AI Stack: Ollama, Open WebUI, and Local RAG
Stand up a fully self-hosted AI stack on a single machine: Ollama for inference, Open WebUI as the chat interface, local embeddings for RAG, and a reverse proxy for secure access. No cloud dependency, no data leaving your network.
17 min readTop 5 Open-Source Coding Models to Run on Your Mac (2026)
The best local coding models for Apple Silicon in 2026, ranked by quality per gigabyte of unified memory. Covers qwen3-coder, devstral, gpt-oss, and more with real pull tags, sizes, and context windows.
14 min readShip Faster. Ship Safer.
Join thousands of engineering teams using MatterAI to autonomously build, review, and deploy code with enterprise-grade precision.
