Bun

HTTP

Note — This page documents the Bun.serve API. This API is heavily optimized and represents the recommended way to build HTTP servers in Bun. Existing Node.js projects may use Bun's nearly complete implementation of the Node.js http and https modules.

Send a request

Bun implements the Web fetch API for making HTTP requests. The fetch function is available in the global scope.

const response = await fetch("https://bun.sh/manifest.json");
const result = (await response.json()) as any;
console.log(result.icons[0].src);
// => "/logo-square.jpg"

Start a server

Start an HTTP server in Bun with Bun.serve.

Bun.serve({
  fetch(req) {
    return new Response(`Bun!`);
  },
});

The fetch handler handles incoming requests. It receives a Request object and returns a Response or Promise<Response>.

Bun.serve({
  fetch(req) {
    const url = new URL(req.url);
    if (url.pathname === "/") return new Response(`Home page!`);
    if (url.pathname === "/blog") return new Response("Blog!");
    return new Response(`404!`);
  },
});

To configure which port and hostname the server will listen on:

Bun.serve({
  port: 8080, // defaults to $PORT, then 3000
  hostname: "mydomain.com", // defaults to "0.0.0.0"
  fetch(req) {
    return new Response(`404!`);
  },
});

Error handling

To activate development mode, set development: true. By default, development mode is enabled unless NODE_ENV is production.

Bun.serve({
  development: true,
  fetch(req) {
    throw new Error("woops!");
  },
});

In development mode, Bun will surface errors in-browser with a built-in error page.

Bun's built-in 500 page

To handle server-side errors, implement an error handler. This function should return a Response to served to the client when an error occurs. This response will supercede Bun's default error page in development mode.

Bun.serve({
  fetch(req) {
    throw new Error("woops!");
  },
  error(error: Error) {
    return new Response(`<pre>${error}\n${error.stack}</pre>`, {
      headers: {
        "Content-Type": "text/html",
      },
    });
  },
});

Note — Full debugger support is planned.

The call to Bun.serve returns a Server object. To stop the server, call the .stop() method.

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

server.stop();

TLS

Bun supports TLS out of the box, powered by OpenSSL. Enable TLS by passing in a value for keyFile and certFile; both are required to enable TLS. If needed, supply a passphrase to decrypt the keyFile.

Bun.serve({
  fetch(req) {
    return new Response("Hello!!!");
  },
  keyFile: "./key.pem", // path to TLS key
  certFile: "./cert.pem", // path to TLS cert
  passphrase: "super-secret", // optional passphrase
});

The root CA and Diffie-Helman parameters can be configured as well.

Bun.serve({
  fetch(req) {
    return new Response("Hello!!!");
  },
  keyFile: "./key.pem", // path to TLS key
  certFile: "./cert.pem", // path to TLS cert
  caFile: "./ca.pem", // path to root CA certificate
  dhParamsFile: "./dhparams.pem", // Diffie Helman parameters
});

Hot reloading

Thus far, the examples on this page have used the explicit Bun.serve API. Bun also supports an alternate syntax.

server.ts
import {type Serve} from "bun";

export default {
  fetch(req) {
    return new Response(`Bun!`);
  },
} satisfies Serve;

Instead of passing the server options into Bun.serve, export it. This file can be executed as-is; when Bun runs a file with a default export containing a fetch handler, it passes it into Bun.serve under the hood.

This syntax has one major advantage: it is hot-reloadable out of the box. When any source file is changed, Bun will reload the server with the updated code without restarting the process. This makes hot reloads nearly instantaneous. Use the --hot flag when starting the server to enable hot reloading.

bun --hot server.ts

It's possible to configure hot reloading while using the explicit Bun.serve API; for details refer to Runtime > Hot reloading.

Streaming files

To stream a file, return a Response object with a BunFile object as the body.

import { serve, file } from "bun";

serve({
  fetch(req) {
    return new Response(Bun.file("./hello.txt"));
  },
});

⚡️ Speed — Bun automatically uses the sendfile(2) system call when possible, enabling zero-copy file transfers in the kernel—the fastest way to send files.

[v0.3.0+] You can send part of a file using the slice(start, end) method on the Bun.file object. This automatically sets the Content-Range and Content-Length headers on the Response object.

Bun.serve({
  fetch(req) {
    // parse `Range` header
    const [start = 0, end = Infinity] = req.headers
      .get("Range") // Range: bytes=0-100
      .split("=") // ["Range: bytes", "0-100"]
      .at(-1) // "0-100"
      .split("-") // ["0", "100"]
      .map(Number); // [0, 100]

    // return a slice of the file
    const bigFile = Bun.file("./big-video.mp4");
    return new Response(bigFile.slice(start, end));
  },
});

Benchmarks

Below are Bun and Node.js implementations of a simple HTTP server that responds Bun! to each incoming Request.

Bun
Node
Bun
Bun.serve({
  fetch(req: Request) {
    return new Response(`Bun!`);
  },
  port: 3000,
});
Node
require("http")
  .createServer((req, res) => res.end("Bun!"))
  .listen(8080);

The Bun.serve server can handle roughly 2.5x more requests per second than Node.js on Linux.

RuntimeRequests per second
Node 16~64,000
Bun~160,000
image

Reference

See TypeScript definitions