4.3 Public API 链路
学习目标
完成本节后,你将能够:
- 解释 Public API 和 tRPC 在鉴权、稳定性和文档上的差异。
- 读懂
withMiddlewares和createAuthedProjectAPIRoute的分工。 - 知道公共 API 变更为什么要同步 Zod schema、Fern 和 generated client。
4.3.1 先给结论
Public API 是外部 contract,不是给 UI 用的内部 convenience route。
它的标准链路是:
text
pages/api/public route -> withMiddlewares -> createAuthedProjectAPIRoute -> service/repository -> schema response这条链路负责:
- CORS 和 HTTP method dispatch;
- 统一错误边界;
- API key / admin API key auth;
- project scope 和 access level;
- rate limit;
- Zod query/body/response;
- OpenTelemetry context;
- dev 环境 response validation;
- 对外 API contract 和 Fern 同步。
4.3.2 链路图
4.3.3 withMiddlewares:HTTP 边界
源码:web/src/features/public-api/server/withMiddlewares.ts
它负责每个 Public API route 的外层 HTTP 行为:
| 行为 | 作用 |
|---|---|
runMiddleware(req, res, cors) | 统一 CORS。 |
| method dispatch | 只允许 route 声明的 GET/POST/PUT/DELETE/PATCH。 |
contextWithLangfuseProps | 设置 ClickHouse surface = publicapi,route 来自 request。 |
ClickHouseResourceError handling | 返回 422 和用户可理解的资源限制提示。 |
| BaseError/Zod/Prisma handling | 统一 public JSON error shape。 |
| unstable error contract | 特定 API surface 可使用不同错误契约。 |
withMiddlewares 是 HTTP 层错误和 method 的外壳,不应该承载业务逻辑。
4.3.4 createAuthedProjectAPIRoute:项目级 API contract
源码:web/src/features/public-api/server/createAuthedProjectAPIRoute.ts
它把一条 project-scoped Public API route 描述成配置:
| 配置项 | 含义 |
|---|---|
name | 日志和调试用 route 名。 |
querySchema | Zod query parser。 |
bodySchema | Zod body parser。 |
responseSchema | Zod response schema,dev 环境会校验。 |
successStatusCode | 默认 200,可覆盖。 |
rateLimitResource | 使用哪类 rate limit bucket。 |
rateLimitUpgradePath | rate limited 时给用户的升级路径。 |
isAdminApiKeyAuthAllowed | self-hosted 下是否允许 admin API key。 |
allowedAccessLevels | 接受 project key、scores key 等哪些 access level。 |
allowInAppAgentKey | 是否允许 in-app agent key 调用。 |
rejectInEventsOnlyMode | v4 events_only 时拒绝读 legacy 表的 endpoint。 |
fn | 真正业务 handler。 |
这个 wrapper 的价值是把“每个 Public API 都必须遵守的边界”统一起来,避免每条 route 自己实现 auth、rate limit 和 schema。
4.3.5 鉴权方式
Public API 不使用 UI session,而是从 Authorization header 验证 API key。
| 方式 | 行为 |
|---|---|
| regular API key | ApiAuthService.verifyAuthHeaderAndReturnScope 返回 project scope、access level、plan、rate limit overrides。 |
| Bearer public key | 可用于某些 scores access level endpoint。 |
| admin API key | 仅 self-hosted,要求 Bearer token、x-langfuse-admin-api-key、x-langfuse-project-id。 |
鉴权结果会变成:
text
auth.scope.projectId
auth.scope.accessLevel
auth.scope.orgId
auth.scope.apiKeyId
auth.scope.publicKey后续 handler 不应该重新从 request 自己猜 project scope。
4.3.6 tRPC vs Public API
| 维度 | tRPC | Public API |
|---|---|---|
| 使用者 | Web UI | SDK、外部用户、自动化脚本 |
| 鉴权 | NextAuth session + project/org membership | API key scope + access level + rate limit |
| 稳定性 | 内部 contract,可随 UI 演进 | 外部 contract,变更要兼容 |
| 校验 | procedure input schema | query/body/response schema |
| 错误格式 | tRPC error | public JSON error contract |
| 文档 | 不需要 Fern | 需要 Fern/API docs/SDK 同步 |
| 版本和迁移 | UI 同步部署 | 可能被旧 SDK 和客户脚本长期调用 |
所以新增外部 endpoint 时,不要复用 tRPC router 作为 Public API。它们面向的 contract 不同。
4.3.7 Public API 变更的同步面
| 改动 | 同步位置 |
|---|---|
| 新增 route | web/src/pages/api/public/**、wrapper、tests。 |
| 新增 query/body field | Zod schema、Fern schema、API docs。 |
| 新增 response field | responseSchema、Fern response、generated clients。 |
| 改错误语义 | withMiddlewares 或 route-specific error contract。 |
| 改 pagination/filter | Public API types、query builder、规模保护。 |
| 改 legacy/v4 数据源 | rejectInEventsOnlyMode、query path、migration docs。 |
Public API 是外部承诺。即使代码能编译,contract 没同步也算未完成。
4.3.8 自检清单
- route 是否包在
withMiddlewares里? - project-scoped route 是否使用
createAuthedProjectAPIRoute? - query/body/response 是否都有 Zod schema?
- handler 是否使用
auth.scope.projectId? - 是否设置了正确的
allowedAccessLevels和rateLimitResource? - response 是否可能过大,是否需要 field selection 或 pagination?
- 是否读 legacy traces/observations,在 events_only 模式下是否要拒绝?
- Fern 和 generated client 是否需要同步?