There's a new HTTP framework in the Bun ecosystem that's worth checking out: Verb. It takes a refreshingly simple approach - leveraging Bun's built-in capabilities instead of reinventing the wheel.
What Makes Verb Interesting
Verb is built directly on Bun's native HTTP server with zero external dependencies. This means you get the raw performance of Bun without any abstraction overhead. Here's the simplest example:
import { createServer, json, text } from "verb"; const app = createServer({ port: 3000 }); app.get("/", () => text("Hello, Verb!")); app.get("/api/users/:id", (req, params) => json({ id: params.id }));
That's a working server. No configuration files, no boilerplate.
Key Features
Intelligent Route Caching
Verb uses an LRU cache for compiled route patterns. After the first request to a route, subsequent matches are nearly instant:
// These patterns are compiled once, then cached app.get("/users/:id", handler); app.get("/posts/:userId/comments/:commentId", handler); app.get("/static/*", handler); // Including wildcards
Built-in Streaming Support
Streaming is first-class in Verb, making it straightforward to handle real-time data, large exports, or progressive loading:
// Server-Sent Events app.get("/events", () => { async function* eventGenerator() { let count = 0; while (count < 100) { yield { data: JSON.stringify({ count, timestamp: Date.now() }), event: "update" }; await new Promise(r => setTimeout(r, 1000)); count++; } } return streamSSE(eventGenerator()); }); // Stream large JSON datasets as JSONL app.get("/api/export", () => { async function* dataGenerator() { for (let i = 0; i < 100000; i++) { yield { id: i, data: `Record ${i}` }; } } return streamJSON(dataGenerator()); });
Middleware System
The middleware API is straightforward and composable:
// Request timing app.use(async (req, next) => { const start = Date.now(); const response = await next(); console.log(`${req.method} ${req.url} took ${Date.now() - start}ms`); return response; }); // Built-in compression app.use(compression()); // Simple auth check app.use(async (req, next) => { if (req.url.startsWith("/api/") && !req.headers.get("authorization")) { return error("Unauthorized", 401); } return next(); });
HTTP/2 Support
Full HTTP/2 support with server push capabilities:
const app = createServer({ http2: true, tls: { cert: "./cert.pem", key: "./key.pem" } }); // Push critical resources with the response app.get("/", () => { return responseWithPush( htmlContent, [ { path: "/styles/critical.css", type: "text/css" }, { path: "/js/app.js", type: "application/javascript" } ] ); });
Testing Utilities
Verb includes a mock server for testing without network overhead:
import { createMockServer } from "verb"; import { expect, test } from "bun:test"; test("user endpoint returns correct data", async () => { const app = createMockServer(); app.get("/users/:id", (req, params) => json({ id: params.id })); const response = await app.request.get("/users/123"); const data = await response.json(); expect(data).toEqual({ id: "123" }); });
Complete Example: REST API
Here's a full CRUD API to show how these pieces fit together:
import { createServer, json, parseBody, error } from "verb"; const app = createServer({ port: 3000 }); // In-memory store const users = new Map(); let nextId = 1; // Enable compression app.use(compression()); // List users app.get("/api/users", () => { return json(Array.from(users.values())); }); // Get specific user app.get("/api/users/:id", (req, params) => { const user = users.get(parseInt(params.id)); if (!user) return error("User not found", 404); return json(user); }); // Create user app.post("/api/users", async (req) => { const body = await parseBody(req); const user = { id: nextId++, ...body }; users.set(user.id, user); return json(user, 201); }); // Update user app.put("/api/users/:id", async (req, params) => { const id = parseInt(params.id); if (!users.has(id)) return error("User not found", 404); const body = await parseBody(req); const user = { id, ...body }; users.set(id, user); return json(user); }); // Delete user app.delete("/api/users/:id", (req, params) => { const id = parseInt(params.id); if (!users.delete(id)) return error("User not found", 404); return new Response(null, { status: 204 }); });
Static File Serving
Verb handles static files with caching, ETags, and compression:
// Serve files from ./public app.get("/static/*", serveStatic({ root: "./public", maxAge: 86400, // 1 day cache immutable: true, // Add immutable directive etag: true, // Generate ETags extensions: [".html"] // Try .html if not found })); // Simpler syntax for basic use cases app.get("/assets/*", staticFiles("./assets"));
Performance Considerations
Since Verb is built directly on Bun's HTTP server:
- No additional parsing or routing layers
- Route caching eliminates regex compilation on hot paths
- Native HTTP/2 and TLS support
- Minimal memory allocations per request
The framework is particularly well-suited for:
- High-throughput APIs
- Real-time applications using SSE or WebSockets
- Services that need to stream large amounts of data
- Microservices where startup time and memory usage matter
Installation and Usage
# Install from GitHub bun add github:wess/verb # Create a simple server echo 'import { createServer, text } from "verb"; const app = createServer({ port: 3000 }); app.get("/", () => text("Hello from Verb!"));' > server.ts # Run it bun server.ts
When to Use Verb
Verb is a good fit if you're:
- Already using Bun or planning to migrate
- Building APIs or microservices
- Need streaming capabilities
- Want minimal dependencies
- Prioritizing performance
It might not be the right choice if you:
- Need to run on Node.js
- Want a full-stack framework with templating, ORM, etc.
- Prefer convention-over-configuration frameworks
Wrapping Up
Verb demonstrates what's possible when you build specifically for Bun's strengths. By avoiding unnecessary abstractions and leveraging native capabilities, it delivers both performance and a clean API.
The project is actively maintained and open to contributions. Check out the GitHub repository for more examples, documentation, and to get involved.
If you're exploring Bun for your next project, Verb is definitely worth a look. It's one of those tools that does one thing really well - serving HTTP requests fast.
Have you tried Verb or other Bun frameworks? What's been your experience? Share your thoughts in the comments.
Top comments (1)
Never used this before but seems like it's worth trying