Skip to content

Você pode adicionar rotas ao Bun.serve() usando a propriedade routes (para caminhos estáticos, parâmetros e wildcards) ou lidando com requisições não correspondidas com o método fetch.

O roteador do Bun.serve() constrói sobre a abordagem baseada em árvore do uWebSocket para adicionar decodificação de parâmetros de rota acelerada por SIMD e cache de estrutura do JavaScriptCore para levar a performance aos limites do que o hardware moderno permite.

Configuração Básica ​

server.ts
ts
Bun.serve({
  routes: {
    "/": () => new Response("Home"),
    "/api": () => Response.json({ success: true }),
    "/users": async () => Response.json({ users: [] }),
  },
  fetch() {
    return new Response("Rota não correspondida");
  },
});

Rotas em Bun.serve() recebem um BunRequest (que estende Request) e retornam uma Response ou Promise<Response>. Isto facilita usar o mesmo código para enviar e receber requisições HTTP.

ts
// Simplificado para brevidade
interface BunRequest<T extends string> extends Request {
  params: Record<T, string>;
  readonly cookies: CookieMap;
}

Rotas Assíncronas ​

Async/await ​

Você pode usar async/await em handlers de rota para retornar uma 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 ​

Você também pode retornar uma Promise<Response> de um handler de rota.

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

Precedência de Rotas ​

Rotas são correspondidas em ordem de especificidade:

  1. Rotas exatas (/users/all)
  2. Rotas com parâmetros (/users/:id)
  3. Rotas wildcard (/users/*)
  4. Global catch-all (/*)
ts
Bun.serve({
  routes: {
    // Mais específico primeiro
    "/api/users/me": () => new Response("Usuário atual"),
    "/api/users/:id": req => new Response(`Usuário ${req.params.id}`),
    "/api/*": () => new Response("API catch-all"),
    "/*": () => new Response("Global catch-all"),
  },
});

Parâmetros de Rota Type-safe ​

TypeScript analisa parâmetros de rota quando passados como um string literal, então seu editor mostrará autocomplete ao acessar request.params.

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

Bun.serve({
  routes: {
    // TypeScript conhece a forma dos params quando passado como string literal
    "/orgs/:orgId/repos/:repoId": req => {
      const { orgId, repoId } = req.params;
      return Response.json({ orgId, repoId });
    },

    "/orgs/:orgId/repos/:repoId/settings": (
      // opcional: você pode explicitamente passar um tipo para BunRequest:
      req: BunRequest<"/orgs/:orgId/repos/:repoId/settings">,
    ) => {
      const { orgId, repoId } = req.params;
      return Response.json({ orgId, repoId });
    },
  },
});

Valores de parâmetros de rota percent-encoded são automaticamente decodificados. Caracteres Unicode são suportados. Unicode inválido é substituído pelo caractere de substituição Unicode &0xFFFD;.

Respostas Estáticas ​

Rotas também podem ser objetos Response (sem a função handler). Bun.serve() otimiza para dispatch de zero alocação - perfeito para health checks, redirecionamentos e conteúdo fixo:

ts
Bun.serve({
  routes: {
    // Health checks
    "/health": new Response("OK"),
    "/ready": new Response("Ready", {
      headers: {
        // Passa headers customizados
        "X-Ready": "1",
      },
    }),

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

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

Respostas de rotas estáticas não alocam memória adicional após inicialização. Você pode geralmente esperar pelo menos 15% de melhoria de performance sobre retornar manualmente um objeto Response.

Respostas de rotas estáticas são cacheadas durante o lifetime do objeto server. Para recarregar rotas estáticas, chame server.reload(options).

Respostas de Arquivo vs Respostas Estáticas ​

Ao servir arquivos em rotas, existem dois comportamentos distintos dependendo se você faz buffer do conteúdo do arquivo ou serve diretamente:

ts
Bun.serve({
  routes: {
    // Rota estática - conteúdo é bufferizado em memória na inicialização
    "/logo.png": new Response(await Bun.file("./logo.png").bytes()),

    // Rota de arquivo - conteúdo é lido do filesystem em cada requisição
    "/download.zip": new Response(Bun.file("./download.zip")),
  },
});

Rotas estáticas (new Response(await file.bytes())) bufferizam conteúdo em memória na inicialização:

  • Zero I/O de filesystem durante requisições - conteúdo servido inteiramente da memória
  • Suporte a ETag - Automaticamente gera e valida ETags para caching
  • If-None-Match - Retorna 304 Not Modified quando ETag do cliente corresponde
  • Sem tratamento de 404 - Arquivos ausentes causam erros na inicialização, não 404s em runtime
  • Uso de memória - Conteúdo completo do arquivo armazenado em RAM
  • Melhor para: Assets estáticos pequenos, respostas de API, arquivos frequentemente acessados

Rotas de arquivo (new Response(Bun.file(path))) leem do filesystem por requisição:

  • Leituras de filesystem em cada requisição - verifica existência do arquivo e lê conteúdo
  • Tratamento de 404 built-in - Retorna 404 Not Found se arquivo não existe ou se torna inacessível
  • Suporte a Last-Modified - Usa tempo de modificação do arquivo para headers If-Modified-Since
  • If-Modified-Since - Retorna 304 Not Modified quando arquivo não mudou desde versão cacheada do cliente
  • Suporte a range requests - Automaticamente lida com requisições de conteúdo parcial com headers Content-Range
  • Transferências streaming - Usa leitor bufferizado com tratamento de backpressure para uso eficiente de memória
  • Eficiente em memória - Apenas bufferiza pequenos chunks durante transferência, não arquivo inteiro
  • Melhor para: Arquivos grandes, conteúdo dinâmico, uploads de usuário, arquivos que mudam frequentemente

Streaming de Arquivos ​

Para fazer stream de um arquivo, retorne um objeto Response com um objeto BunFile como body.

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

Você pode enviar parte de um arquivo usando o método slice(start, end) no objeto Bun.file. Isto automaticamente define os headers Content-Range e Content-Length no objeto Response.

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

    // retorna um slice do arquivo
    const bigFile = Bun.file("./big-video.mp4");
    return new Response(bigFile.slice(start, end));
  },
});

Handler de requisição fetch ​

O handler fetch lida com requisições recebidas que não foram correspondidas por nenhuma rota. Ele recebe um objeto Request e retorna uma Response ou Promise<Response>.

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

O handler fetch suporta 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(`Dormiu por ${end - start}ms`);
  },
});

Respostas baseadas em Promise também são suportadas:

ts
Bun.serve({
  fetch(req) {
    // Encaminha a requisição para outro servidor.
    return fetch("https://example.com");
  },
});

Você também pode acessar o objeto Server do handler fetch. É o segundo argumento passado para a função fetch.

ts
// `server` é passado como segundo argumento para `fetch`.
const server = Bun.serve({
  fetch(req, server) {
    const ip = server.requestIP(req);
    return new Response(`Seu IP é ${ip.address}`);
  },
});

Bun by www.bunjs.com.cn edit