> ## Documentation Index
> Fetch the complete documentation index at: https://bun.ll1025.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# 路由

> 在 `Bun.serve` 中使用静态路径、参数和通配符定义路由

使用 `Bun.serve()` 的 `routes` 属性添加路由（静态路径、参数和通配符），或使用 [`fetch`](#fetch) 方法处理未匹配的请求。

`Bun.serve()` 的路由器基于 uWebSocket 的[树形方法](https://github.com/oven-sh/bun/blob/0d1a00fa0f7830f8ecd99c027fce8096c9d459b6/packages/bun-uws/src/HttpRouter.h#L57-L64)，增加了 [SIMD 加速的路由参数解码](https://github.com/oven-sh/bun/blob/main/src/jsc/bindings/decodeURIComponentSIMD.cpp#L21-L271)和 [JavaScriptCore 结构缓存](https://github.com/oven-sh/bun/blob/main/src/jsc/bindings/ServerRouteList.cpp#L100-L101)，以进一步突破现代硬件的性能极限。

## 基本设置

```ts title="server.ts" icon="https://mintcdn.com/span-inc/82N53aP7NFbaCVSl/icons/typescript.svg?fit=max&auto=format&n=82N53aP7NFbaCVSl&q=85&s=787d39d6a7d96d7f9540dc74344eba23" theme={null}
Bun.serve({
  routes: {
    "/": () => new Response("首页"),
    "/api": () => Response.json({ success: true }),
    "/users": async () => Response.json({ users: [] }),
  },
  fetch() {
    return new Response("未匹配的路由");
  },
});
```

`Bun.serve()` 中的路由接收一个 `BunRequest`（它扩展了 [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)）并返回一个 [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) 或 `Promise<Response>`。这使得在发送和接收 HTTP 请求时使用相同的代码变得更加容易。

```ts theme={null}
// 为简洁起见，进行了简化
interface BunRequest<T extends string> extends Request {
  params: Record<T, string>;
  readonly cookies: CookieMap;
}
```

## 异步路由

### Async/await

在路由处理器中使用 async/await 来返回 `Promise<Response>`。

```ts theme={null}
import { sql, serve } from "bun";

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

### Promise

你也可以从路由处理器中返回 `Promise<Response>`。

```ts theme={null}
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);
      });
    },
  },
});
```

***

## 路由优先级

路由按优先级顺序匹配：

1. 精确路由（`/users/all`）
2. 参数路由（`/users/:id`）
3. 通配符路由（`/users/*`）
4. 全局捕获路由（`/*`）

```ts theme={null}
Bun.serve({
  routes: {
    // 最具体的优先
    "/api/users/me": () => new Response("当前用户"),
    "/api/users/:id": req => new Response(`用户 ${req.params.id}`),
    "/api/*": () => new Response("API 捕获全部"),
    "/*": () => new Response("全局捕获全部"),
  },
});
```

***

## 类型安全的路由参数

当路径作为字符串字面量传递时，TypeScript 会解析路由参数，因此在访问 `request.params` 时编辑器会显示自动补全。

```ts title="index.ts" icon="https://mintcdn.com/span-inc/82N53aP7NFbaCVSl/icons/typescript.svg?fit=max&auto=format&n=82N53aP7NFbaCVSl&q=85&s=787d39d6a7d96d7f9540dc74344eba23" theme={null}
import type { BunRequest } from "bun";

Bun.serve({
  routes: {
    // 当作为字符串字面量传递时，TypeScript 知道 params 的形状
    "/orgs/:orgId/repos/:repoId": req => {
      const { orgId, repoId } = req.params;
      return Response.json({ orgId, repoId });
    },

    "/orgs/:orgId/repos/:repoId/settings": (
      // 可选：你可以显式地向 BunRequest 传递类型：
      req: BunRequest<"/orgs/:orgId/repos/:repoId/settings">,
    ) => {
      const { orgId, repoId } = req.params;
      return Response.json({ orgId, repoId });
    },
  },
});
```

Bun 会自动解码百分比编码的路由参数值，包括 Unicode 字符。无效的 Unicode 将被替换为 Unicode 替换字符（`\uFFFD`）。

### 静态响应

路由也可以是 `Response` 对象（无需处理器函数）。`Bun.serve()` 会对其进行零分配调度优化，适用于健康检查、重定向和固定内容：

```ts theme={null}
Bun.serve({
  routes: {
    // 健康检查
    "/health": new Response("OK"),
    "/ready": new Response("就绪", {
      headers: {
        // 传递自定义头部
        "X-Ready": "1",
      },
    }),

    // 重定向
    "/blog": Response.redirect("https://bun.com/blog"),

    // API 响应
    "/api/config": Response.json({
      version: "1.0.0",
      env: "production",
    }),
  },
});
```

静态响应在初始化后不会分配额外内存。通常，至少比手动返回 `Response` 对象有 15% 的性能提升。

静态路由响应在服务器对象的生命周期内被缓存。要重新加载静态路由，请调用 `server.reload(options)`。

### 文件响应 vs 静态响应

从路由提供文件服务时，行为取决于你是将文件内容缓冲到内存中还是直接提供文件：

```ts theme={null}
Bun.serve({
  routes: {
    // 静态路由 - 启动时内容被缓冲到内存中
    "/logo.png": new Response(await Bun.file("./logo.png").bytes()),

    // 文件路由 - 每次请求时从文件系统读取内容
    "/download.zip": new Response(Bun.file("./download.zip")),
  },
});
```

**静态路由**（`new Response(await file.bytes())`）在启动时将内容缓冲到内存中：

* **请求期间零文件系统 I/O**——内容完全从内存提供
* **ETag 支持**——自动生成和验证 ETag 用于缓存
* **If-None-Match**——当客户端 ETag 匹配时返回 `304 Not Modified`
* **没有 404 处理**——文件缺失会导致启动时错误，而非运行时 404
* **内存使用**——完整文件内容存储在 RAM 中
* **最适合**：小型静态资源、API 响应、频繁访问的文件

**文件路由**（`new Response(Bun.file(path))`）每次请求都从文件系统读取：

* **每次请求都进行文件系统读取**——检查文件是否存在并读取内容
* **内置 404 处理**——如果文件不存在或无法访问，返回 `404 Not Found`
* **Last-Modified 支持**——使用文件修改时间用于 `If-Modified-Since` 头部
* **If-Modified-Since**——自客户端缓存版本以来文件未更改时返回 `304 Not Modified`
* **范围请求支持**——自动处理带有 `Content-Range` 头部的部分内容请求
* **流式传输**——使用支持背压的缓冲读取器，内存效率高
* **内存高效**——传输期间仅缓冲小块数据，而非整个文件
* **最适合**：大文件、动态内容、用户上传、频繁变化的文件

***

## 流式传输文件

要流式传输文件，返回一个以 `BunFile` 对象作为主体的 `Response` 对象。

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

<Info>
  ⚡️ **速度** — Bun 在可能的情况下自动使用 [`sendfile(2)`](https://man7.org/linux/man-pages/man2/sendfile.2.html)
  系统调用，在内核中实现零拷贝文件传输——这是发送文件的最快方式。
</Info>

要发送文件的一部分，使用 `Bun.file` 对象上的 [`slice(start, end)`](https://developer.mozilla.org/en-US/docs/Web/API/Blob/slice) 方法。Bun 会自动在 `Response` 对象上设置 `Content-Range` 和 `Content-Length` 头部。

```ts theme={null}
Bun.serve({
  fetch(req) {
    // 解析 `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]

    // 返回文件的一个切片
    const bigFile = Bun.file("./big-video.mp4");
    return new Response(bigFile.slice(start, end));
  },
});
```

***

## `fetch` 请求处理器

`fetch` 处理器对没有匹配到路由的传入请求运行。它接收一个 [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) 对象，并返回一个 [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) 或 [`Promise<Response>`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)。

```ts theme={null}
Bun.serve({
  fetch(req) {
    const url = new URL(req.url);
    if (url.pathname === "/") return new Response("首页!");
    if (url.pathname === "/blog") return new Response("博客!");
    return new Response("404!");
  },
});
```

`fetch` 处理器支持 async/await：

```ts theme={null}
import { sleep, serve } from "bun";

serve({
  async fetch(req) {
    const start = performance.now();
    await sleep(10);
    const end = performance.now();
    return new Response(`休眠了 ${end - start}ms`);
  },
});
```

也支持基于 Promise 的响应：

```ts theme={null}
Bun.serve({
  fetch(req) {
    // 将请求转发到另一台服务器。
    return fetch("https://example.com");
  },
});
```

`fetch` 处理器还将 `Server` 对象作为其第二个参数接收。

```ts theme={null}
// `server` 作为 `fetch` 的第二个参数传入。
const server = Bun.serve({
  fetch(req, server) {
    const ip = server.requestIP(req);
    return new Response(`你的 IP 是 ${ip.address}`);
  },
});
```
