Skip to content

Sie können Routen zu Bun.serve() hinzufügen, indem Sie die routes-Eigenschaft verwenden (für statische Pfade, Parameter und Wildcards) oder nicht übereinstimmende Anfragen mit der fetch-Methode behandeln.

Der Router von Bun.serve() baut auf dem baumbasierten Ansatz von uWebSocket auf, um SIMD-beschleungte Routenparameter-Dekodierung und JavaScriptCore-Struktur-Caching hinzuzufügen, um die Leistungsgrenzen dessen zu erweitern, was moderne Hardware ermöglicht.

Grundlegende Einrichtung

server.ts
ts
Bun.serve({
  routes: {
    "/": () => new Response("Startseite"),
    "/api": () => Response.json({ success: true }),
    "/users": async () => Response.json({ users: [] }),
  },
  fetch() {
    return new Response("Nicht übereinstimmende Route");
  },
});

Routen in Bun.serve() erhalten eine BunRequest (die Request erweitert) und geben eine Response oder Promise<Response> zurück. Dies erleichtert die Verwendung desselben Codes zum Senden und Empfangen von HTTP-Anfragen.

ts
// Zur Kürze vereinfacht
interface BunRequest<T extends string> extends Request {
  params: Record<T, string>;
  readonly cookies: CookieMap;
}

Asynchrone Routen

Async/await

Sie können async/await in Routenhandlern verwenden, um eine Promise<Response> zurückzugeben.

ts
import { sql, serve } from "bun";

serve({
  port: 3001,
  routes: {
    "/api/version": async () => {
      const [version] = await sql`SELECT version()`;
      return Response.json(version);
    },
  },
});

Promise

Sie können auch eine Promise<Response> von einem Routenhandler zurückgeben.

ts
import { sql, serve } from "bun";

serve({
  routes: {
    "/api/version": () => {
      return new Promise(resolve => {
        setTimeout(async () => {
          const [version] = await sql`SELECT version()`;
          resolve(Response.json(version));
        }, 100);
      });
    },
  },
});

Routenpriorität

Routen werden in der Reihenfolge der Spezifität abgeglichen:

  1. Exakte Routen (/users/all)
  2. Parameterrouten (/users/:id)
  3. Wildcard-Routen (/users/*)
  4. Globale Catch-All-Route (/*)
ts
Bun.serve({
  routes: {
    // Am spezifischsten zuerst
    "/api/users/me": () => new Response("Aktueller Benutzer"),
    "/api/users/:id": req => new Response(`Benutzer ${req.params.id}`),
    "/api/*": () => new Response("API-Catch-All"),
    "/*": () => new Response("Globaler Catch-All"),
  },
});

Typsichere Routenparameter

TypeScript analysiert Routenparameter, wenn sie als String-Literal übergeben werden, sodass Ihr Editor Autovervollständigung beim Zugriff auf request.params anzeigt.

index.ts
ts
import type { BunRequest } from "bun";

Bun.serve({
  routes: {
    // TypeScript kennt die Form von params, wenn als String-Literal übergeben
    "/orgs/:orgId/repos/:repoId": req => {
      const { orgId, repoId } = req.params;
      return Response.json({ orgId, repoId });
    },

    "/orgs/:orgId/repos/:repoId/settings": (
      // optional: Sie können explizit einen Typ an BunRequest übergeben:
      req: BunRequest<"/orgs/:orgId/repos/:repoId/settings">,
    ) => {
      const { orgId, repoId } = req.params;
      return Response.json({ orgId, repoId });
    },
  },
});

Prozent-kodierte Routenparameterwerte werden automatisch dekodiert. Unicode-Zeichen werden unterstützt. Ungültiges Unicode wird durch das Unicode-Ersatzzeichen &0xFFFD; ersetzt.

Statische Antworten

Routen können auch Response-Objekte sein (ohne die Handler-Funktion). Bun.serve() optimiert dies für die Null-Allokation-Dispatch – perfekt für Health-Checks, Weiterleitungen und festen Inhalt:

ts
Bun.serve({
  routes: {
    // Health-Checks
    "/health": new Response("OK"),
    "/ready": new Response("Bereit", {
      headers: {
        // Benutzerdefinierte Header übergeben
        "X-Ready": "1",
      },
    }),

    // Weiterleitungen
    "/blog": Response.redirect("https://bun.com/blog"),

    // API-Antworten
    "/api/config": Response.json({
      version: "1.0.0",
      env: "production",
    }),
  },
});

Statische Antworten allozieren nach der Initialisierung keinen zusätzlichen Speicher. Sie können im Allgemeinen mindestens eine 15%ige Leistungsverbesserung gegenüber dem manuellen Zurückgeben eines Response-Objekts erwarten.

Statische Routenantworten werden für die Lebensdauer des Server-Objekts zwischengespeichert. Um statische Routen neu zu laden, rufen Sie server.reload(options) auf.

Datei-Antworten vs. statische Antworten

Beim Bereitstellen von Dateien in Routen gibt es zwei unterschiedliche Verhaltensweisen, je nachdem, ob Sie den Dateiinhalt puffern oder direkt bereitstellen:

ts
Bun.serve({
  routes: {
    // Statische Route - Inhalt wird beim Start im Speicher gepuffert
    "/logo.png": new Response(await Bun.file("./logo.png").bytes()),

    // Datei-Route - Inhalt wird bei jeder Anfrage vom Dateisystem gelesen
    "/download.zip": new Response(Bun.file("./download.zip")),
  },
});

Statische Routen (new Response(await file.bytes())) puffern Inhalt beim Start im Speicher:

  • Kein Dateisystem-I/O während Anfragen – Inhalt wird vollständig aus dem Speicher bereitgestellt
  • ETag-Unterstützung – Generiert und validiert automatisch ETags für Caching
  • If-None-Match – Gibt 304 Not Modified zurück, wenn Client-ETag übereinstimmt
  • Keine 404-Behandlung – Fehlende Dateien verursachen Startfehler, nicht Laufzeit-404s
  • Speichernutzung – Vollständiger Dateiinhalt im RAM gespeichert
  • Am besten für: Kleine statische Assets, API-Antworten, häufig abgerufene Dateien

Datei-Routen (new Response(Bun.file(path))) lesen bei jeder Anfrage vom Dateisystem:

  • Dateisystem-Lesevorgänge bei jeder Anfrage – prüft Dateiexistenz und liest Inhalt
  • Integrierte 404-Behandlung – Gibt 404 Not Found zurück, wenn Datei nicht existiert oder nicht zugänglich ist
  • Last-Modified-Unterstützung – Verwendet Datei-Änderungszeit für If-Modified-Since-Header
  • If-Modified-Since – Gibt 304 Not Modified zurück, wenn Datei sich seit der im Client-Cache gespeicherten Version nicht geändert hat
  • Range-Request-Unterstützung – Verarbeitet automatisch Teilinhalt-Anfragen mit Content-Range-Headern
  • Streaming-Übertragungen – Verwendet gepufferten Leser mit Backpressure-Handling für effiziente Speichernutzung
  • Speichereffizient – Puffert nur kleineChunks während der Übertragung, nicht die gesamte Datei
  • Am besten für: Große Dateien, dynamische Inhalte, Benutzer-Uploads, Dateien, die sich häufig ändern

Streaming von Dateien

Um eine Datei zu streamen, geben Sie ein Response-Objekt mit einem BunFile-Objekt als Body zurück.

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

Sie können einen Teil einer Datei mit der slice(start, end)-Methode auf dem Bun.file-Objekt senden. Dies setzt automatisch die Content-Range- und Content-Length-Header auf dem Response-Objekt.

ts
Bun.serve({
  fetch(req) {
    // `Range`-Header parsen
    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]

    // einen Ausschnitt der Datei zurückgeben
    const bigFile = Bun.file("./big-video.mp4");
    return new Response(bigFile.slice(start, end));
  },
});

fetch Anfrage-Handler

Der fetch-Handler behandelt eingehende Anfragen, die von keiner Route übereinstimmt wurden. Er erhält ein Request-Objekt und gibt eine Response oder Promise<Response> zurück.

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

Der fetch-Handler unterstützt async/await:

ts
import { sleep, serve } from "bun";

serve({
  async fetch(req) {
    const start = performance.now();
    await sleep(10);
    const end = performance.now();
    return new Response(`Für ${end - start}ms geschlafen`);
  },
});

Promise-basierte Antworten werden ebenfalls unterstützt:

ts
Bun.serve({
  fetch(req) {
    // Die Anfrage an einen anderen Server weiterleiten.
    return fetch("https://example.com");
  },
});

Sie können auch auf das Server-Objekt vom fetch-Handler zugreifen. Es ist das zweite Argument, das an die fetch-Funktion übergeben wird.

ts
// `server` wird als zweites Argument an `fetch` übergeben.
const server = Bun.serve({
  fetch(req, server) {
    const ip = server.requestIP(req);
    return new Response(`Ihre IP ist ${ip.address}`);
  },
});

Bun von www.bunjs.com.cn bearbeitet