Request ID
@vafast/request-id 为每个请求生成唯一 ID:写入 req.id、经 next({ requestId }) 注入 handler 上下文,并回写到响应头(默认 X-Request-Id)。
安装
bash
npm install @vafast/request-id快速开始
typescript
import { Server, defineRoute, defineRoutes, serve } from 'vafast'
import { requestId } from '@vafast/request-id'
const routes = defineRoutes([
defineRoute({
method: 'GET',
path: '/',
handler: ({ requestId: id, req }) => ({
fromContext: id,
fromReq: req.id,
}),
}),
])
const server = new Server(routes)
server.use(requestId())
serve({ fetch: server.fetch, port: 3000 })响应会带上:
X-Request-Id: 550e8400-e29b-41d4-a716-446655440000用法
从 handler 读取
中间件执行 next({ requestId }),handler 可直接解构;同时也挂在 req.id:
typescript
defineRoute({
method: 'GET',
path: '/work',
handler: ({ requestId: id, req }) => {
console.log(id, req.id) // 同一值
return { ok: true }
},
})类型安全辅助
typescript
import { getRequestId } from '@vafast/request-id'
const id = getRequestId(req) // string | undefined自定义生成器 / 响应头
typescript
import { requestId } from '@vafast/request-id'
server.use(
requestId({
generator: () => `req-${Date.now()}`,
headerName: 'X-Correlation-Id',
}),
)分布式追踪(复用上游 ID)
默认 useExisting: true:若入站请求已有同名头,则复用,不再生成。
typescript
server.use(
requestId({
headerName: 'X-Request-Id',
existingHeaderName: 'X-Trace-Id', // 从另一请求头读取
}),
)
// 始终生成新 ID
server.use(requestId({ useExisting: false }))与 request-logger 配合
typescript
import { requestId } from '@vafast/request-id'
import { requestLogger } from '@vafast/request-logger'
server.use(requestId()) // 先挂,写入 req.id
server.use(
requestLogger({
url: process.env.LOG_INGEST_URL!,
service: 'my-server',
}),
)request-logger 会优先读 req.id,再回退到 x-request-id 头。
API完整参数
requestId(options?)
返回 Vafast 中间件。
RequestIdOptions
| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
generator | () => string | crypto.randomUUID() | 自定义 ID 生成器 |
headerName | string | 'X-Request-Id' | 写入响应头的名称 |
useExisting | boolean | true | 是否复用入站请求头中的 ID |
existingHeaderName | string | 同 headerName | 读取已有 ID 的请求头名称 |
行为摘要:
- 若
useExisting,从existingHeaderName读入站 ID - 没有则调用
generator() - 赋给
req.id,并next({ requestId: id }) - 克隆响应,设置
headerName后返回
getRequestId(req)
typescript
getRequestId(req: Request): string | undefined读取 req.id,未挂中间件时为 undefined。
类型
typescript
type IdGenerator = () => string
interface RequestIdOptions {
generator?: IdGenerator
headerName?: string
useExisting?: boolean
existingHeaderName?: string
}包还通过 declare global 给 Request 增加可选 id?: string。
最佳实践
- 尽早挂载:放在需要关联日志的中间件之前(尤其是
request-logger) - 链路统一头名:网关与服务约定同一
X-Request-Id,保持useExisting: true - 业务日志带上 ID:
logger.info({ requestId: id }, '...'),便于与访问日志关联 - 需要短 ID 时用自定义
generator(如 nanoid),不要改全局crypto
注意事项
- 中间件会新建 Response 以写入响应头;原
response.body流会被转发,勿在下游重复消费后依赖同一 body useExisting: true时信任入站头内容,不做格式校验——网关侧应校验或清洗@vafast/logger不是请求中间件;应用日志需自行带上requestId- handler 里同时可用上下文
requestId与req.id,二者为同一字符串