我的好朋友 Claude
第 120 期|Claude Code|創作者|

Claude Code 自動維護 API docs:OpenAPI spec、TypeScript types、example response 同步

API 改完,docs 永遠落後幾個版本。教用 Claude Code 同步 OpenAPI spec、TypeScript types、example response 三層 docs,pre-commit hook 自動跑、PR review 必有 docs diff。

難度 ★★★時間 50 分鐘用具 Claude Code CLI、你個 API project (Express / Hono / FastAPI / etc.)
【編者撰】一個香港人

情境

你個 B2B API 上咗 prod 半年。Endpoint 由 12 條變 47 條。

文件呢?

結果:客戶 integration team 第三次寫 email 投訴「why does the response shape not match your docs?」。Frontend 同事每隔幾日就撞一次 type mismatch。

問題唔係冇人寫 docs,係docs 同 code 根本唔係同一個 source of truth。每次靠人手對齊,總有一層漏。

呢篇教用 Claude Code 建立 3-tier doc sync

  1. OpenAPI spec —— single source of truth,由 route handler 直接生成
  2. TypeScript types —— spec 出 codegen,frontend / SDK 直接 import
  3. Example payload —— Claude 跑落 staging、核實個 response,再更新 example

加埋一個 pre-commit hook + /api-doc slash command —— 加新 endpoint,docs 自動跟。

跟住做

1. Route → OpenAPI spec 自動 sync

第一層 source of truth 係 openapi.yaml,但唔係手寫,係由 route file 推導出嚟。

假設你用 Hono / Express,route 大概係咁:

// src/routes/orders.ts
app.post('/api/orders', async (c) => {
  const body = CreateOrderSchema.parse(await c.req.json())
  const order = await createOrder(body)
  return c.json({ success: true, data: order })
})

關鍵:Zod schema 已經喺度,OpenAPI 要嘅資訊就喺 schema 入面。

加個 CLAUDE.md rule 落 project root:

## OpenAPI Sync Rule

每次加 / 改 route handler,必須同步更新 openapi.yaml:
1. Route path + method 對應 paths section
2. Zod schema 用 zod-to-openapi 轉換做 requestBody / responseBody
3. 唔好手寫 schema —— 永遠由 Zod source 推
4. 如果 Zod schema 改咗 field,openapi.yaml 對應 section 必須同步

Claude Code 讀到呢條 rule,你下次叫佢加 endpoint 嘅時候,佢就會自動更新 spec

裝 zod-to-openapi:

npm install @asteasolutions/zod-to-openapi

呢個 lib 等 Zod schema 直接吐出 OpenAPI fragment,Claude 唔使再重複寫多次 schema。

2. TypeScript types codegen

OpenAPI 有咗,frontend types 直接由佢生出嚟。

npm install -D openapi-typescript

加條 script 落 package.json

{
  "scripts": {
    "gen:types": "openapi-typescript openapi.yaml -o src/types/api.ts"
  }
}

跑:

pnpm gen:types

src/types/api.ts 入面會出晒所有 endpoint 嘅 request / response 型別:

export interface paths {
  '/api/orders': {
    post: {
      requestBody: { content: { 'application/json': components['schemas']['CreateOrder'] } }
      responses: { 200: { content: { 'application/json': components['schemas']['Order'] } } }
    }
  }
}

Frontend / SDK 直接 import:

import type { paths } from '@/types/api'
type CreateOrderBody = paths['/api/orders']['post']['requestBody']['content']['application/json']

人手 copy field 嘅日子玩完。 Spec 改一行,types 即刻跟。

3. Example payload test + 核實 pipeline

最後一層:example response。呢層最易過時,因為冇人想親手跑 endpoint 再 copy 個 JSON 出嚟。

寫條 script scripts/refresh-examples.ts

import { spec } from '../openapi'

for (const endpoint of spec.paths) {
  const response = await fetch(`https://staging.api.example.com${endpoint.path}`, {
    method: endpoint.method,
    headers: { Authorization: `Bearer ${process.env.STAGING_TOKEN}` },
    body: endpoint.exampleRequest,
  })
  const data = await response.json()
  await writeFile(`docs/examples/${endpoint.id}.json`, JSON.stringify(data, null, 2))
}

跑:

pnpm tsx scripts/refresh-examples.ts

跟住叫 Claude Code 核實:

Verify example payload prompt
讀 docs/examples/ 入面所有 JSON 同 openapi.yaml 嘅 response schema。對比每一個 example:
有冇缺 field(spec 講有、example 冇)
有冇多 field(example 有、spec 冇)—— 通常代表 spec 落後
Field type 有冇對唔上
逐個 endpoint 出 diff report,列明邊個要改邊一層。

Claude 會逐個檢、出一份 markdown report、指明「呢個 example 嘅 deprecated field 要移除」或者「spec 漏咗 created_at」。

呢個三層循環跑完一次,docs 同 code 就完全對齊。

4. /api-doc slash command 一句話加新 endpoint

最後加個 project-level slash command。喺 .claude/commands/api-doc.md

---
description: 加新 API endpoint + 自動 sync 三層 docs
---

加新 endpoint 嘅 workflow:

1. 喺 src/routes/ 加 route handler(Zod schema 必寫)
2. Run pnpm openapi:emit —— openapi.yaml 自動更新
3. Run pnpm gen:types —— TypeScript types 自動 codegen
4. Run pnpm tsx scripts/refresh-examples.ts —— example payload 跑落 staging
5. Verify:對比 example vs spec,出 diff
6. Commit:openapi.yaml / types / examples 三層同 route handler 一齊入 PR

用家會用一句話 describe endpoint。你完成 1-6 步先返人。

之後 Claude Code 入面:

/api-doc 加一個 GET /api/orders/:id endpoint,攞返單筆訂單詳情,permission 同 POST 一樣

Claude 會跑晒成條鏈:route → schema → spec → types → example → 核實 → commit。

收工。

加埋一個 pre-commit hook(.husky/pre-commit):

#!/bin/sh
pnpm openapi:emit
pnpm gen:types
git diff --exit-code openapi.yaml src/types/api.ts || {
  echo "❌ Docs out of sync. Commit auto-updated files."
  exit 1
}

任何 PR 想入 main,docs 唔同步就直接擋住。

變化

變化 1:GraphQL schema 版(vs REST)

GraphQL 唔使用 OpenAPI —— schema.graphql 本身就係 source of truth。但同步 example query / response 嘅問題一樣存在。

Claude Code workflow:

讀 schema.graphql,對每個 Query / Mutation 生成:
1. Example query (with fragments)
2. 落 staging 跑、record response
3. 寫入 docs/graphql-examples/

加 codegen(@graphql-codegen/cli)出 TypeScript types,做法同 REST 版完全一樣。

變化 2:Python FastAPI 版(Pydantic 同 schema)

FastAPI 識自己吐 OpenAPI —— /openapi.json 即係 source of truth。

curl http://localhost:8000/openapi.json > openapi.json

之後成條鏈同 TypeScript 版一樣:openapi-typescript-codegen 出 client types,Claude 核實 example。

關鍵:Pydantic model 同 endpoint signature 已經夠表達,唔使再加多一層。Claude 直接讀 router file,spec 就跟住 import 入嚟嘅 Pydantic schema 同步。

變化 3:Webhook docs(asymmetric send/receive)

Webhook 同 REST 唔同 —— 係你 send 出去,但收嘅人要知你個 payload shape。

呢類 docs 要分兩面:

用 AsyncAPI spec(即係 OpenAPI 嘅 event 版本)。Claude Code workflow:

讀 src/webhooks/ 嘅 publisher code,提取每個 event 嘅:
- Topic / event name
- Payload Zod schema
- Retry policy
出 asyncapi.yaml,跟住生 markdown docs 畀 partner team

Partner 接 webhook 嘅時候,永遠睇返自動同步嗰份 docs。

拆解:點解 work,同邊度會仆街

跟到上面就已經用得。下面呢段係畀想由「跑一次 sync OK」做到「成個 team 用足一年都信得過」嘅人——初學者可以跳過,唔影響你跟住做。

自動化 docs 最唔老實嘅地方係:第一次 generate 對晒,唔代表第三十次 commit 都對。呢條三層鏈,實際會喺呢幾個位仆街,你要預咗:

1. example refresh 跑落 staging 但攞錯或者攞唔到資料

refresh-examples.ts 假設每個 endpoint 都用同一個 token、同一份 example request 就 call 得通。實際上 POST 要 valid body、/api/orders/:id 要一個真係存在嘅 id、有啲 endpoint 仲要前置狀態(要先有張單先 GET 到)。

2. Claude 對比 example vs spec 嗰陣「眼瞓」漏咗 field

verify prompt 叫 Claude 逐個 endpoint 對 field。但 response 一深(nested object、array of object、data.items[].metadata)、endpoint 一多,LLM 唔係逐 byte diff,係靠理解,長 context 之下會靜靜漏報。

3. pre-commit hook 一慢,大家就 --no-verify 繞過

個 hook 每次 commit 都 openapi:emitgen:types,project 一大,呢兩步可能要跑好幾秒。開發者趕住 commit,好快就學識 git commit --no-verify,個攔截形同虛設。

4. emit 出嚟嘅 diff 順序唔穩,逼到次次都有 noise

zod-to-openapi 或者 codegen 工具如果唔保證 output 嘅 key 順序穩定(或者你升咗版),同一份 source 生出嚟嘅 yaml 可能行序唔同。pre-commit 嗰句 git diff --exit-code 就會次次都話「out of sync」,就算你乜都冇改。

5. 改 Zod schema 改壞咗,等於一鍵把錯誤推落三層

呢套嘢嘅威力係 single source 一改、三層全跟。但反過嚟講——你改 Zod schema 改錯咗(打多隻字、type 寫錯),個錯就會自動 codegen 落 types、寫入 example、出晒去 partner docs,一路冇人 review。

呢幾個位,就係「跑一次 sync OK」同「47 條 endpoint 用足一年都信得過」之間嘅距離。

一個心態

API docs 過時嘅根源唔係懶 —— 係 docs 同 code 本身就分開兩個 source。靠人手對齊,一定有漏。

呢個 workflow 嘅重點:將 single source 嘅範圍縮到淨返一層(route handler 入面嘅 Zod schema),其餘幾層全部係 derived artifacts。Derived 出嚟嘅嘢 Claude 識生,唔使人手維護。

下個 sprint 加新 endpoint 嘅時候,試吓 /api-doc 一句話搞掂佢。半年之後再望返轉頭,47 條 endpoint 嘅 docs 仲係同 code 對齊。客戶 integration team 都唔再寫投訴 email。

文中工具 · 連結

  • 開發者用 — terminal 入面同 Claude pair coding

睇完想同 Claude 一齊行一次?

撳一撳,就將成段 tutor 指示(連埋成篇文嘅內容)抄入剪貼簿。 貼入 Claude.ai 或 Claude Desktop,佢會用廣東話帶你一步一步行, 每步問你填關鍵位,最後畀返一個專為你情況寫嘅 prompt 帶走。

下期預告 · 相關情境
訂閱本副刊

每週日早上,
一道新菜送到你 inbox。

一篇 use case、一個香港情境、一個跟得到嘅做法。 冇 sell course、冇話你「再唔學就會失業」。

訂閱通道執緊緊
newsletter service 仲未接通。想第一時間收到新文章——
直接 email 我哋寫一句「訂閱」就得。

Email 「訂閱」畀我