Bun.serve HTTP server API
Bun.serve() is the canonical API for starting an HTTP server in Bun. It takes a configuration object with a fetch handler that receives a Request and returns a Response. Example: Bun.serve({ fetch(req: Request) { return new Response("Success!"); } })
bunfig.toml serve.port setting
The [serve] section has a port field that sets the default port for Bun.serve to listen on. Default is 3000. Can also be set with BUN_PORT or PORT environment variables or the --port flag.
Example: Bun.serve error handler with stack trace
Bun.serve({
fetch(req) {
throw new Error("woops!");
},
error(error) {
return new Response(`<pre>${error}\n${error.stack}</pre>`, {
headers: {
"Content-Type": "text/html",
},
});
},
});
Bun.serve development mode enables built-in error page
Setting development: true in Bun.serve activates development mode, which displays Bun's built-in error page in the browser when errors occur in the fetch handler.
Bun.serve error callback handler
The error callback in Bun.serve receives an error object and should return a Response to serve to the client when a fetch handler throws an error. In development mode, this response replaces Bun's default error page. The error object contains the error details and stack trace.
server.subscriberCount(topic) method
The server object has a subscriberCount method that takes a topic string as an argument and returns the number of WebSocket subscribers for that topic.
server.pendingWebSockets counter
The server object passed to the fetch handler has a pendingWebSockets property that returns the number of currently active WebSocket connections.
WebSocket topic subscription with ws.subscribe
WebSocket connections can subscribe to topics using the ws.subscribe() method in the websocket message handler. This allows grouped messaging to multiple WebSocket clients.
Example: Monitor server metrics with Bun.serve
const server = Bun.serve({
fetch(req, server) {
return new Response(
`Active requests: ${server.pendingRequests}\n` + `Active WebSockets: ${server.pendingWebSockets}`,
);
},
});
server.pendingRequests counter
The server object passed to the fetch handler has a pendingRequests property that returns the number of currently active HTTP requests.
TLS passphrase for encrypted keys
If your private key is encrypted with a passphrase, provide the passphrase value in the tls object to decrypt it.
Bun.serve TLS configuration basic
To enable TLS in Bun.serve, pass a tls object with key and cert fields. Both key and cert expect the contents of your TLS key and certificate, not a path to it. Each can be a string, BunFile, TypedArray, or Buffer.
TLS key and cert input types
The key and cert fields in Bun.serve's tls object accept: BunFile (from Bun.file()), Buffer (from fs.readFileSync()), string (UTF-8 encoded), or an array of any of the above types.
Bun.serve SNI multiple servers example
Example of configuring multiple server names with different certificates:
```ts
Bun.serve({
tls: [
{
key: Bun.file("./key1.pem"),
cert: Bun.file("./cert1.pem"),
serverName: "my-server1.com",
},
{
key: Bun.file("./key2.pem"),
cert: Bun.file("./cert2.pem"),
serverName: "my-server2.com",
},
],
});
```
Bun.serve TLS with CA and Diffie-Hellman example
Example of enabling TLS with CA certificate and Diffie-Hellman parameters:
```ts
Bun.serve({
tls: {
key: Bun.file("./key.pem"),
cert: Bun.file("./cert.pem"),
ca: Bun.file("./ca.pem"),
dhParamsFile: "/path/to/dhparams.pem",
},
});
```
Bun.serve TLS with passphrase example
Example of enabling TLS with an encrypted private key:
```ts
Bun.serve({
tls: {
key: Bun.file("./key.pem"),
cert: Bun.file("./cert.pem"),
passphrase: "my-secret-passphrase",
},
});
```
Bun.serve TLS basic example
Example of enabling TLS with key and cert:
```ts
Bun.serve({
tls: {
key: Bun.file("./key.pem"),
cert: Bun.file("./cert.pem"),
},
});
```
TLS powered by BoringSSL
Bun's TLS support is built-in and powered by BoringSSL.
Server name indication (SNI) multiple
To support multiple server names, pass an array of tls configuration objects to Bun.serve's tls option. Each object in the array should have key, cert, and serverName fields.
Server name indication (SNI) single
To configure server name indication (SNI) for the server, set the serverName field in the tls object with a domain name string.
TLS Diffie-Hellman parameters
To override Diffie-Hellman parameters, pass dhParamsFile with a path to a Diffie Hellman parameters file in PEM format to the tls object.
TLS ca override for trusted certificates
Pass ca in the tls object to override the trusted CA certificates. By default, the server trusts the list of well-known CAs curated by Mozilla; setting ca replaces that list entirely.
Bun.serve example export default syntax
import type { Serve } from 'bun';
export default {
fetch(req) {
return new Response('Bun!');
},
} satisfies Serve.Options<undefined>;
Bun.serve export default syntax
Instead of calling Bun.serve() directly, you can export a default object containing the server configuration. Bun will automatically detect a file with a 'default' export containing a 'fetch' handler and pass it into Bun.serve(). The exported object should satisfy the Serve.Options type, with a type parameter indicating the WebSocket data type (use 'undefined' if not using custom WebSocket data).
Bun.serve hot route reloading
Call server.reload() with new route and handler definitions to update the server's routes without restarting the server. This enables zero-downtime deployments.
server.stop() method
Call server.stop() to stop the server from accepting new connections. By default, it allows in-flight requests and WebSocket connections to complete; idle keep-alive connections are closed immediately. Pass 'true' as an argument to immediately terminate all connections instead. Returns a promise that resolves once every connection has closed.
server.closeIdleConnections() method
Call server.closeIdleConnections() to close keep-alive connections that are not currently serving a request, without stopping the server. Connections with a request in flight and open WebSockets are untouched, and the server continues accepting new connections. Returns the number of connections that were closed.
server.ref() and server.unref() methods
Call server.unref() to make the server not keep the Bun process alive (allowing the process to exit if the server is the only thing running). Call server.ref() to restore the default behavior where the server keeps the process alive.
server.reload() method
Call server.reload() to update the server's handlers without restarting. Pass a new configuration object with updated 'fetch', 'error', 'routes', or 'websocket' handlers. Only these four handlers can be updated; other options are ignored.
server.timeout(request, seconds) per-request timeout
Call server.timeout(request, seconds) to override the idle timeout for an individual request. Pass 0 to disable the timeout entirely for that request. This is useful for long-lived streaming responses like Server-Sent Events that should not be closed if no bytes are sent for longer than the global idleTimeout.
server.requestIP(request) get client IP and port
Call server.requestIP(request) to get the client's IP address and port information. Returns an object with 'address' and 'port' properties, or null for closed requests or Unix domain sockets.
server.pendingRequests and server.pendingWebSockets metrics
server.pendingRequests is a read-only property indicating the number of in-flight HTTP requests. server.pendingWebSockets is a read-only property indicating the number of active WebSocket connections. These can be used to monitor server activity.
server.subscriberCount(topic) WebSocket topic subscribers
Call server.subscriberCount(topic) to get the count of WebSocket clients currently subscribed to a given topic.
Server properties: url, port, hostname, development, id
The server object has read-only properties: url (Server URL including protocol, hostname and port), port (port the server is listening on), hostname (hostname the server is bound to), development (boolean indicating whether server is in development mode), and id (server instance identifier).
WebSocketHandler interface configuration
The websocket handler in Bun.serve accepts a WebSocketHandler object with the following options: maxPayloadLength (maximum WebSocket message size in bytes), backpressureLimit (bytes of queued messages before applying backpressure), closeOnBackpressureLimit (whether to close connection when backpressure limit hit), idleTimeout (seconds before idle timeout), perMessageDeflate (enable per-message deflate compression, boolean or object with compress/decompress properties), sendPings (send ping frames to keep connection alive), publishToSelf (whether server receives its own published messages). Lifecycle methods: open (called when connection opened), message (called when message received), close (called when connection closed), ping (called when ping frame received), pong (called when pong frame received), drain (called when backpressure is relieved).
TLSOptions interface for Bun.serve
The tls option in Bun.serve accepts a TLSOptions object with properties: ca (certificate authority chain, string/Buffer/BunFile/array), cert (server certificate, string/Buffer/BunFile/array), key (private key, string/Buffer/BunFile/array), passphrase (private key passphrase, string), dhParamsFile (path to DH parameters file), serverName (server name for SNI), lowMemoryMode (reduce TLS memory usage, boolean), secureOptions (OpenSSL options flags, number).
Bun.serve example basic HTTP server
Bun.serve({
fetch(req) {
return new Response('Bun!');
},
port: 3000,
});
Bun.serve example with routes basic
const server = Bun.serve({
routes: {
'/api/status': new Response('OK'),
'/users/:id': req => {
return new Response(`Hello User ${req.params.id}!`);
},
'/api/posts': {
GET: () => new Response('List posts'),
POST: async req => {
const body = await req.json();
return Response.json({ created: true, ...body });
},
},
'/api/*': Response.json({ message: 'Not found' }, { status: 404 }),
'/blog/hello': Response.redirect('/blog/hello/world'),
'/favicon.ico': Bun.file('./favicon.ico'),
},
fetch(req) {
return new Response('Not Found', { status: 404 });
},
});
console.log(`Server running at ${server.url}`);
Bun.serve example HTML imports
import myReactSinglePageApp from './index.html';
Bun.serve({
routes: {
'/': myReactSinglePageApp,
},
});
Bun.serve example port and hostname configuration
Bun.serve({
port: 8080,
hostname: 'mydomain.com',
fetch(req) {
return new Response('404!');
},
});
Bun.serve example random port
const server = Bun.serve({
port: 0,
fetch(req) {
return new Response('404!');
},
});
console.log(server.port);
Bun.serve example unix domain socket
Bun.serve({
unix: '/tmp/my-socket.sock',
fetch(req) {
return new Response('404!');
},
});
Bun.serve example abstract namespace socket Linux
Bun.serve({
unix: '\0my-abstract-socket',
fetch(req) {
return new Response('404!');
},
});
Bun.serve example HTTP/3 QUIC with TLS
Bun.serve({
tls: {
key: Bun.file('./key.pem'),
cert: Bun.file('./cert.pem'),
},
http3: true,
fetch(req) {
return new Response('Hello over HTTP/3!');
},
});
Bun.serve example HTTP/3 only without HTTP/1.1
Bun.serve({
tls: {
key: Bun.file('./key.pem'),
cert: Bun.file('./cert.pem'),
},
http3: true,
http1: false,
fetch(req) {
return new Response('HTTP/3 only');
},
});
Bun.serve example idleTimeout configuration
Bun.serve({
idleTimeout: 30,
fetch(req) {
return new Response('Bun!');
},
});
Bun.serve example hot route reloading
const server = Bun.serve({
routes: {
'/api/version': () => Response.json({ version: '1.0.0' }),
},
});
server.reload({
routes: {
'/api/version': () => Response.json({ version: '2.0.0' }),
},
});
server.stop() example graceful and forced stop
const server = Bun.serve({
fetch(req) {
return new Response('Hello!');
},
});
await server.stop();
await server.stop(true);
server.closeIdleConnections() example
const closed = server.closeIdleConnections();
console.log(`closed ${closed} idle connections`);
server.ref() and server.unref() example
server.unref();
server.ref();
server.reload() example update handlers
const server = Bun.serve({
routes: {
'/api/version': Response.json({ version: 'v1' }),
},
fetch(req) {
return new Response('v1');
},
});
server.reload({
routes: {
'/api/version': Response.json({ version: 'v2' }),
},
fetch(req) {
return new Response('v2');
},
});
server.timeout(request, seconds) example
const server = Bun.serve({
async fetch(req, server) {
server.timeout(req, 60);
await req.text();
return new Response('Done!');
},
});
server.timeout for streaming Server-Sent Events
Bun.serve({
routes: {
'/events': (req, server) => {
server.timeout(req, 0);
return new Response(
async function* () {
yield 'data: hello\n\n';
},
{ headers: { 'Content-Type': 'text/event-stream' } },
);
},
},
});
server.requestIP(request) example
const server = Bun.serve({
fetch(req, server) {
const address = server.requestIP(req);
if (address) {
return new Response(`Client IP: ${address.address}, Port: ${address.port}`);
}
return new Response('Unknown client');
},
});
server.pendingRequests and server.pendingWebSockets example
const server = Bun.serve({
fetch(req, server) {
return new Response(
`Active requests: ${server.pendingRequests}\n` + `Active WebSockets: ${server.pendingWebSockets}`,
);
},
});
server.subscriberCount(topic) example
const server = Bun.serve({
fetch(req, server) {
const chatUsers = server.subscriberCount('chat');
return new Response(`${chatUsers} users in chat`);
},
websocket: {
message(ws) {
ws.subscribe('chat');
},
},
});
REST API example with SQLite database
import type { Post } from './types.ts';
import { Database } from 'bun:sqlite';
const db = new Database('posts.db');
db.exec(`
CREATE TABLE IF NOT EXISTS posts (
id TEXT PRIMARY KEY,
title TEXT NOT NULL,
content TEXT NOT NULL,
created_at TEXT NOT NULL
)
`);
Bun.serve({
routes: {
'/api/posts': {
GET: () => {
const posts = db.query('SELECT * FROM posts').all();
return Response.json(posts);
},
POST: async req => {
const post: Omit<Post, 'id' | 'created_at'> = await req.json();
const id = crypto.randomUUID();
db.query(
`INSERT INTO posts (id, title, content, created_at)
VALUES (?, ?, ?, ?)`,
).run(id, post.title, post.content, new Date().toISOString());
return Response.json({ id, ...post }, { status: 201 });
},
},
'/api/posts/:id': req => {
const post = db.query('SELECT * FROM posts WHERE id = ?').get(req.params.id);
if (!post) {
return new Response('Not Found', { status: 404 });
}
return Response.json(post);
},
},
error(error) {
console.error(error);
return new Response('Internal Server Error', { status: 500 });
},
});
Bun.serve performance benchmark
Bun.serve handles roughly 2.5x more requests per second than Node.js on Linux. Node 16 handles approximately 64,000 requests per second. Bun handles approximately 160,000 requests per second.
Server interface fetch method for testing
The server object has a fetch(request) method that can make a request to the running server. Useful for testing or internal routing. Accepts a Request object or string and returns Response or Promise<Response>.
Server interface upgrade method for WebSocket
The server object has an upgrade<T>(request, options?) method to upgrade an HTTP request to a WebSocket connection. The options parameter accepts headers (Bun.HeadersInit) and data (custom data object of type T). Returns true if upgrade successful, false if failed.