Skip to content

Vous pouvez ajouter des routes à Bun.serve() en utilisant la propriété routes (pour les chemins statiques, les paramètres et les jokers) ou en gérant les requêtes non correspondantes avec la méthode fetch.

Le routeur de Bun.serve() s'appuie sur l'approche arborescente de uWebSocket pour ajouter un décodage des paramètres de route accéléré par SIMD et la mise en cache de structure JavaScriptCore pour repousser les limites de performance de ce que le matériel moderne permet.

Configuration de base

ts
Bun.serve({
  routes: {
    "/": () => new Response("Accueil"),
    "/api": () => Response.json({ success: true }),
    "/users": async () => Response.json({ users: [] }),
  },
  fetch() {
    return new Response("Route non correspondante");
  },
});

Les routes dans Bun.serve() reçoivent un BunRequest (qui étend Request) et retournent une Response ou Promise<Response>. Cela facilite l'utilisation du même code pour envoyer et recevoir des requêtes HTTP.

ts
// Simplifié pour la brièveté
interface BunRequest<T extends string> extends Request {
  params: Record<T, string>;
  readonly cookies: CookieMap;
}

Routes asynchrones

Async/await

Vous pouvez utiliser async/await dans les gestionnaires de routes pour retourner une Promise<Response>.

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

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

Promise

Vous pouvez également retourner une Promise<Response> depuis un gestionnaire de route.

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);
      });
    },
  },
});

Priorité des routes

Les routes sont correspondues dans l'ordre de spécificité :

  1. Routes exactes (/users/all)
  2. Routes avec paramètres (/users/:id)
  3. Routes joker (/users/*)
  4. Capture globale (/*)
ts
Bun.serve({
  routes: {
    // Plus spécifique en premier
    "/api/users/me": () => new Response("Utilisateur actuel"),
    "/api/users/:id": req => new Response(`Utilisateur ${req.params.id}`),
    "/api/*": () => new Response("Capture API"),
    "/*": () => new Response("Capture globale"),
  },
});

Paramètres de route typés

TypeScript analyse les paramètres de route lorsqu'ils sont passés comme littéral de chaîne, afin que votre éditeur affiche l'autocomplétion lors de l'accès à request.params.

ts
import type { BunRequest } from "bun";

Bun.serve({
  routes: {
    // TypeScript connaît la forme des params lorsqu'ils sont passés comme littéral de chaîne
    "/orgs/:orgId/repos/:repoId": req => {
      const { orgId, repoId } = req.params;
      return Response.json({ orgId, repoId });
    },

    "/orgs/:orgId/repos/:repoId/settings": (
      // optionnel : vous pouvez explicitement passer un type à BunRequest :
      req: BunRequest<"/orgs/:orgId/repos/:repoId/settings">,
    ) => {
      const { orgId, repoId } = req.params;
      return Response.json({ orgId, repoId });
    },
  },
});

Les valeurs des paramètres de route encodées en pourcentage sont automatiquement décodées. Les caractères Unicode sont pris en charge. L'Unicode invalide est remplacé par le caractère de remplacement Unicode &0xFFFD;.

Réponses statiques

Les routes peuvent également être des objets Response (sans la fonction de gestionnaire). Bun.serve() l'optimise pour une distribution sans allocation - parfait pour les vérifications d'état, les redirections et le contenu fixe :

ts
Bun.serve({
  routes: {
    // Vérifications d'état
    "/health": new Response("OK"),
    "/ready": new Response("Prêt", {
      headers: {
        // Passer des en-têtes personnalisés
        "X-Ready": "1",
      },
    }),

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

    // Réponses API
    "/api/config": Response.json({
      version: "1.0.0",
      env: "production",
    }),
  },
});

Les réponses statiques n'allouent pas de mémoire supplémentaire après l'initialisation. Vous pouvez généralement vous attendre à une amélioration des performances d'au moins 15 % par rapport au retour manuel d'un objet Response.

Les réponses de route statiques sont mises en cache pendant toute la durée de vie de l'objet serveur. Pour recharger les routes statiques, appelez server.reload(options).

Réponses de fichier vs Réponses statiques

Lors du service de fichiers dans les routes, il y a deux comportements distincts selon que vous mettez en mémoire tampon le contenu du fichier ou le servez directement :

ts
Bun.serve({
  routes: {
    // Route statique - le contenu est mis en mémoire tampon au démarrage
    "/logo.png": new Response(await Bun.file("./logo.png").bytes()),

    // Route de fichier - le contenu est lu depuis le système de fichiers à chaque requête
    "/download.zip": new Response(Bun.file("./download.zip")),
  },
});

Les routes statiques (new Response(await file.bytes())) mettent en mémoire tampon le contenu au démarrage :

  • Aucune E/S de système de fichiers pendant les requêtes - le contenu est servi entièrement depuis la mémoire
  • Prise en charge ETag - Génère et valide automatiquement les ETags pour la mise en cache
  • If-None-Match - Retourne 304 Not Modified lorsque l'ETag du client correspond
  • Aucune gestion 404 - Les fichiers manquants provoquent des erreurs au démarrage, pas des 404 à l'exécution
  • Utilisation de la mémoire - Contenu complet du fichier stocké en RAM
  • Meilleur pour : Petits actifs statiques, réponses API, fichiers fréquemment consultés

Les routes de fichier (new Response(Bun.file(path))) lisent depuis le système de fichiers à chaque requête :

  • Lectures de système de fichiers à chaque requête - vérifie l'existence du fichier et lit le contenu
  • Gestion 404 intégrée - Retourne 404 Not Found si le fichier n'existe pas ou devient inaccessible
  • Prise en charge Last-Modified - Utilise l'heure de modification du fichier pour les en-têtes If-Modified-Since
  • If-Modified-Since - Retourne 304 Not Modified lorsque le fichier n'a pas changé depuis la version mise en cache du client
  • Prise en charge des requêtes Range - Gère automatiquement les requêtes de contenu partiel avec les en-têtes Content-Range
  • Transferts en streaming - Utilise un lecteur tamponné avec gestion de la contre-pression pour une utilisation efficace de la mémoire
  • Efficace en mémoire - Ne met en mémoire tampon que de petits morceaux pendant le transfert, pas le fichier entier
  • Meilleur pour : Fichiers volumineux, contenu dynamique, téléchargements d'utilisateurs, fichiers qui changent fréquemment

Streaming de fichiers

Pour streamer un fichier, retournez un objet Response avec un objet BunFile comme corps.

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

Vous pouvez envoyer une partie d'un fichier en utilisant la méthode slice(start, end) sur l'objet Bun.file. Cela définit automatiquement les en-têtes Content-Range et Content-Length sur l'objet Response.

ts
Bun.serve({
  fetch(req) {
    // analyser l'en-tête `Range`
    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]

    // retourner une tranche du fichier
    const bigFile = Bun.file("./big-video.mp4");
    return new Response(bigFile.slice(start, end));
  },
});

Gestionnaire de requête fetch

Le gestionnaire fetch gère les requêtes entrantes qui n'ont été correspondues par aucune route. Il reçoit un objet Request et retourne une Response ou Promise<Response>.

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

Le gestionnaire fetch prend en charge 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(`Endormi pendant ${end - start}ms`);
  },
});

Les réponses basées sur Promise sont également prises en charge :

ts
Bun.serve({
  fetch(req) {
    // Transférer la requête à un autre serveur.
    return fetch("https://example.com");
  },
});

Vous pouvez également accéder à l'objet Server depuis le gestionnaire fetch. C'est le deuxième argument passé à la fonction fetch.

ts
// `server` est passé comme deuxième argument à `fetch`.
const server = Bun.serve({
  fetch(req, server) {
    const ip = server.requestIP(req);
    return new Response(`Votre IP est ${ip.address}`);
  },
});

Bun édité par www.bunjs.com.cn