想去哪里?

首页 页面 关于 页面 项目 页面 简历 页面 技术 页面 随笔 页面 联系 页面 隐私政策 页面 Terminal Spike 隐私政策 页面 用 Astro 构建文档与职业个人网站 技术 通过 tmux、Mosh、Termius 与 Tailscale 随处使用 Codex 技术 用 Langfuse 观察 Codex 的 MCP 工具调用 技术 产品应用中的本地化 技术 把 MCP 作为安全的 AI 集成边界 技术 用 Zod、OpenAPI 与 Swagger 维护 API 契约 技术 用 pg-boss 运行持久后台任务 技术 通过真实知识工作流理解 pgvector 与 RAG 技术 Pi Edge:让 Pi 待在 Obsidian 笔记旁 技术 为真实排序任务设计拖放交互 技术 Prisma 与 PostgreSQL:产品状态的事实来源 技术 把 Ralph Loop 当作计划队列 技术 用 React Router 组织全栈产品工作流 技术 把 shadcn 风格组件变成自己的产品系统 技术 用表格与编辑器承载密集业务操作 技术 Terminal Spike:原生 Android 终端 技术 在 Linux 上用 fcitx 输入带声调的拼音 技术 在 Linux 上配置 DEFT Pro 轨迹球 技术 在 KOReader 上使用 Pocket 稍后读 技术 为 Vercel AI SDK 设置明确的工具边界 技术 垂直切片架构与 dependency-cruiser 技术 用 Vitest 与 Playwright 测试产品工作流 技术 我的随处工作流:Herdr、code-server、Obsidian 与 Pi Edge 技术 Zod 不只是校验 技术 《卡拉马佐夫兄弟》是我看过最好的小说 随笔 基督徒口述1960劳改历史 随笔 圣经中的“道”与言语 随笔 我们爱,因为神先爱我们,而不是空洞的“自爱” 随笔 妈妈,让我来承担爸爸的过错吧 随笔 我与神的故事 随笔 世界远离上帝,接近中国 随笔 “爱的五种语言”和内外向,只存在于语言中 随笔 人格类型不存在,你在世界而非概念中生活 随笔 危险的词语:为什么心理学是不可能的 随笔 2000 年,一个深圳打工者的故事 随笔 阿联酋Ras al Khaimah见闻录 随笔 沙甸:1975 年的一场冲突 随笔 关于中国的纪录片 随笔
← 技术/工程实践

用 Zod、OpenAPI 与 Swagger 维护 API 契约

运行时校验与可读文档,如何避免公开 API 的行为逐渐偏离约定。

公开 API 不只是后端代码,也是面向其他开发者的产品界面。

因此契约既要可读,也要在运行时执行。应用中的类型有帮助,但外部脚本仍可能发送错误结构、漏掉字段或调用旧版接口。

我喜欢在边界使用 Zod,明确预期输入:

const CreateRequestSchema = z.object({
  title: z.string().min(1),
  priority: z.enum(["low", "normal", "high"]).default("normal"),
});

例子刻意保持通用。重点在于服务器校验真正跨过边界的数据,而不是信任客户端某处看起来正确的 TypeScript 类型。

OpenAPI 解决另一个问题:让契约可见。开发者无需阅读服务端源码,就应能了解认证要求、请求体、响应结构和错误格式。

Swagger 等文档界面让人查看并试用 API。这不仅方便,也能让预期行为更容易发现,从而减少支持问题。

我尤其重视错误结构。校验失败也是产品响应,应当稳定到其他系统可以处理:

{
  "error": {
    "code": "validation_failed",
    "message": "The request body is invalid.",
    "fields": {
      "title": "Title is required."
    }
  }
}

格式可以不同,但必须一致。一个接口返回 message,另一个返回 errors,再一个只返回字符串,会让每个集成都变脆弱。

主要风险是契约漂移:校验说一套,文档说另一套;OpenAPI 更新落后于代码,或重构时被遗忘。所以我尽量从运行时使用的同一套 schema 生成契约,至少也要测试文档中的示例。

工具也有代价。产品还不需要公开或合作伙伴 API 时,完整 Swagger 系统可能过重。只供应用内部调用的路由未必需要它,但其他开发者或脚本依赖行为时,这份清晰度值得。

我的 API 边界检查包括:运行时校验、请求与响应文档、可见的认证要求、稳定错误结构、一个真实示例,以及覆盖契约路径而不只是成功路径的测试。

并非每个项目都要“API 优先”。先有网页应用,再补 API 也没问题。但一旦 API 存在,就值得像页面一样认真对待。它仍是用户界面,只是用户恰好是另一个程序。