Skip to main content
Bun provides a fast, built-in HTTP server via Bun.serve(). It’s powered by a fork of uWebSockets and includes support for HTTP/1.1, HTTP/2, HTTPS, WebSockets, and Server-Sent Events.

Basic HTTP Server

const server = Bun.serve({
  port: 3000,
  fetch(req) {
    return new Response("Welcome to Bun!");
  },
});

console.log(`Listening on ${server.url}`);

Server Configuration

Port and Hostname

Bun.serve({
  port: 8080, // Default: 3000
  hostname: "0.0.0.0", // Default: "0.0.0.0" (all interfaces)
  fetch(req) {
    return new Response("Hello");
  },
});

Unix Domain Sockets

Bun.serve({
  unix: "/tmp/bun.sock",
  fetch(req) {
    return new Response("Unix socket server");
  },
});

Request Handling

Accessing Request Data

Bun.serve({
  async fetch(req) {
    const url = new URL(req.url);
    const method = req.method;
    const headers = req.headers;
    
    // Parse request body
    const body = await req.text();
    // or: await req.json();
    // or: await req.formData();
    // or: await req.arrayBuffer();
    
    return new Response(`${method} ${url.pathname}`);
  },
});

Route Parameters with BunRequest

import type { BunRequest } from "bun";

Bun.serve({
  routes: {
    "/users/:id": (req: BunRequest<"/users/:id">) => {
      return new Response(`User ID: ${req.params.id}`);
    },
  },
});

Response Types

Text Response

return new Response("Hello World", {
  headers: { "Content-Type": "text/plain" },
});

JSON Response

return Response.json({ message: "Hello" });

File Response

return new Response(Bun.file("./index.html"));

Stream Response

const stream = new ReadableStream({
  start(controller) {
    controller.enqueue("Hello ");
    controller.enqueue("World");
    controller.close();
  },
});

return new Response(stream);

HTTPS/TLS

Bun.serve({
  port: 443,
  tls: {
    cert: Bun.file("./cert.pem"),
    key: Bun.file("./key.pem"),
    ca: Bun.file("./ca.pem"), // Optional
  },
  fetch(req) {
    return new Response("Secure!");
  },
});

Error Handling

Bun.serve({
  fetch(req) {
    throw new Error("Something went wrong");
  },
  error(error) {
    return new Response(`Error: ${error.message}`, {
      status: 500,
    });
  },
});

Server Object

The Bun.serve() function returns a Server object:
const server = Bun.serve({
  fetch(req) {
    return new Response("Hello");
  },
});

// Server properties
server.port; // 3000
server.hostname; // "0.0.0.0"
server.url; // URL { href: "http://0.0.0.0:3000/" }
server.protocol; // "http" | "https" | null
server.development; // boolean
server.id; // string
server.pendingRequests; // number
server.pendingWebSockets; // number

// Stop the server
await server.stop();

// Stop and close all connections
await server.stop(true);

Hot Reloading

const server = Bun.serve({
  id: "my-server", // Required for hot reload
  fetch(req) {
    return new Response("Hello");
  },
});

// Reload the server with new handlers
server.reload({
  fetch(req) {
    return new Response("Updated!");
  },
});

Development Mode

Bun.serve({
  development: true, // Shows detailed error pages
  fetch(req) {
    return new Response("Dev mode");
  },
});

Request IP and Timeout

Bun.serve({
  fetch(req, server) {
    // Get client IP address
    const address = server.requestIP(req);
    console.log(address); // SocketAddress { address: "127.0.0.1", port: 54321, family: "IPv4" }
    
    // Set request timeout (in seconds)
    server.timeout(req, 30);
    
    return new Response("OK");
  },
});

Publishing to WebSockets

You can publish messages to WebSocket topics from the HTTP handler:
Bun.serve({
  websocket: {
    message(ws, message) {
      ws.subscribe("chat");
    },
  },
  fetch(req, server) {
    if (req.method === "POST") {
      const message = await req.text();
      // Publish to all subscribers
      server.publish("chat", message);
      return new Response("Published");
    }
    return new Response("Send POST");
  },
});

Reference Counting

const server = Bun.serve({
  fetch(req) {
    return new Response("Hello");
  },
});

// Don't keep process alive
server.unref();

// Keep process alive again
server.ref();

Type Signatures

interface Server<WebSocketData> {
  readonly port: number | undefined;
  readonly hostname: string | undefined;
  readonly url: URL;
  readonly protocol: "http" | "https" | null;
  readonly development: boolean;
  readonly id: string;
  readonly pendingRequests: number;
  readonly pendingWebSockets: number;
  
  stop(closeActiveConnections?: boolean): Promise<void>;
  reload<R extends string>(options: Serve.Options<WebSocketData, R>): Server<WebSocketData>;
  upgrade(request: Request, options?: { headers?: HeadersInit; data?: WebSocketData }): boolean;
  publish(topic: string, data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer, compress?: boolean): number;
  subscriberCount(topic: string): number;
  requestIP(request: Request): SocketAddress | null;
  timeout(request: Request, seconds: number): void;
  ref(): void;
  unref(): void;
}

Build docs developers (and LLMs) love