API 文件產生器
從現有的路由處理器程式碼中提取 API 文件。讀取您的原始碼,推斷結構,並產生 OpenAPI 規格、TypeScript 型別與可執行的 curl 範例——不是從設計意圖,而是從實際的實作。
觸發條件
啟動關鍵字: "generate API docs", "OpenAPI from code", "document endpoints", "API reference", "swagger generation", "endpoint documentation", "create API types", "curl examples for API"
不適用於: 從頭開始的 API 設計 -> api-architect | 無原始碼的 OpenAPI 編寫 -> openapi-spec-writer | API 測試 -> test-automation-expert
核心功能
- 從 Express、Next.js App Router、Next.js Pages API 路由、Fastify 與 Hono 處理器中提取路由定義
- 從 TypeScript 型別、Zod 結構或執行時期驗證推斷請求/回應結構
- 產生包含精確路徑參數、查詢字串與請求主體的 OpenAPI 3.0/3.1 規格
- 產出與實際 API 合約相符的 TypeScript 型別定義
- 為每個端點建立附有實際範例資料的 curl 範例
- 透過追蹤中介軟體鏈來記錄身份驗證需求
- 識別速率限制設定並記錄每個端點的限制
- 透過分析處理器中的 throw/return 模式來分類錯誤回應
- 偵測未文件化的端點(存在路由但無 JSDoc 或結構)
框架偵測
在產生任何內容之前,先識別框架。提取策略差異顯著。
Express / Express 風格
信號:app.get(), router.post(), express.Router()
路由來源:app._router.stack 或明確的路由檔案
中介軟體鏈:app.use() 的順序對身份驗證偵測很重要
尋找路由註冊模式:
router.get('/users/:id', authenticate, getUser)—— 處理器前的中介軟體表示需要身份驗證app.use('/api/v1', rateLimiter({ max: 100 }), apiRouter)—— 速率限制器應用於子樹
Next.js App Router
信號:route.ts 檔案導出 GET, POST, PUT, DELETE, PATCH
路由來源:app/api/ 中的檔案系統慣例
中介軟體:目錄邊界的 middleware.ts
路徑參數來自資料夾名稱:app/api/users/[id]/route.ts --> /api/users/{id}
Next.js Pages API 路由
信號:pages/api/**/*.ts 帶有預設導出處理器
路由來源:檔案系統慣例
方法路由:處理器內的 req.method switch
Fastify
信號:fastify.get(), fastify.route(), 路由選項上的 schema 屬性
路由來源:使用 fastify.register() 的插件註冊
結構:Fastify 路由通常已經有 JSON Schema —— 提取並轉換
Fastify 是最容易文件化的框架,因為路由經常宣告自己的結構。在推斷之前,優先提取現有的 schema.body、schema.response 和 schema.querystring。
Hono
信號:app.get(), app.post(), Hono(), zValidator()
路由來源:Hono 實例上的方法鏈接
驗證:透過 zValidator 中介軟體的基於 zod 的驗證
使用 zValidator 的 Hono 直接為您提供 Zod 結構。轉換 Zod --> JSON Schema --> OpenAPI 結構。
提取流程
步驟 1:發現路由
掃描專案中的路由註冊。不要依賴單一入口點——框架經常將路由分散到多個檔案中。
# Express:尋找路由器檔案
grep -rl "express.Router\|app.get\|app.post\|router\." src/ --include="*.ts" --include="*.js"
# Next.js App Router:尋找路由處理器
find src/app/api -name "route.ts" -o -name "route.js"
# Next.js Pages:尋找 API 路由
find src/pages/api -name "*.ts" -o -name "*.js"
# Fastify:尋找路由註冊
grep -rl "fastify\.\(get\|post\|put\|delete\|route\)" src/ --include="*.ts"
# Hono:尋找路由定義
grep -rl "app\.\(get\|post\|put\|delete\|patch\)" src/ --include="*.ts"
步驟 2:提取結構
對於每個路由,識別請求和回應的形狀。
結構來源的優先順序:
- 明確的 Zod/Joi/Yup 驗證結構(最高信心度)
- 請求/回應上的 TypeScript 型別註解
- JSON Schema 宣告(Fastify)
- JSDoc @param / @returns 註解
- 從回應建構的執行時期推斷(最低信心度——標記為近似值)
使用執行時期推斷(選項 5)時,請務必在 OpenAPI 輸出中將結構標記為 x-inferred: true,以便消費者知道它可能不完整。
步驟 3:追蹤身份驗證
遍歷每個路由的中介軟體鏈以確定身份驗證需求:
- 無身份驗證中介軟體:標記為
security: [](公開) - Bearer Token 中介軟體:標記為
security: [{ bearerAuth: [] }] - API Key 中介軟體:標記為
security: [{ apiKeyAuth: [] }] - Session/Cookie 身份驗證:標記為
security: [{ cookieAuth: [] }] - 多種身份驗證選項:將所有選項列為安全性陣列中的替代方案
步驟 4:分類錯誤
閱讀每個處理器並列出每個錯誤回應:
res.status(400).json(...)或throw new BadRequestError(...)--> 400 回應結構res.status(401)--> 401(即使沒有明確擲出,如果存在身份驗證中介軟體也包含在內)res.status(404)--> 404res.status(409)--> 409(衝突,常見於建立操作)res.status(422)--> 422(驗證失敗)res.status(429)--> 429(速率限制,如果偵測到速率限制中介軟體則包含在內)res.status(500)--> 500(始終包含可能性)
步驟 5:產生輸出
產出三個成品:
- OpenAPI 規格(YAML,非 JSON——更易於閱讀,更容易比對差異)
- TypeScript 型別(獨立的
.d.ts檔案,包含所有請求/回應型別) - curl 範例(每個端點一個,帶有標頭和範例負載)
決策點
當程式碼庫沒有驗證結構時
如果處理器接受未經 Zod/Joi 驗證的原始 req.body,您必須從使用情況推斷型別。尋找:
- 屬性存取模式:
req.body.name、req.body.email-->{ name: string, email: string } - 解構:
const { name, email } = req.body--> 相同的推斷 - 資料庫插入呼叫:
db.users.create({ name: body.name })--> 如果可用,交叉參考資料庫結構
在資訊層級用警告標記整個規格:x-schemas-inferred: true。
當路由使用動態路徑段時
- Express:
:id--> OpenAPI{id} - Next.js:
[id]--> OpenAPI{id} - Next.js 捕獲所有:
[...slug]--> OpenAPI{slug}並附註陣列行為 - Fastify:
:id--> OpenAPI{id} - Hono:
:id--> OpenAPI{id}
當回應形狀因狀態碼而異時
為每個狀態碼文件化其自己的結構。如果 200 和 201 不同,不要將它們合併成一個結構。常見模式:
responses:
'200':
description: 使用者已找到
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'404':
description: 使用者未找到
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
當每個端點的速率限制不同時
使用 OpenAPI 擴充功能文件化速率限制:
x-rate-limit:
requests: 100
window: 60s
scope: per-api-key
如果速率限制器是全域的(在應用層級套用一次),請在頂層的 info 區段文件化。如果是每個路由的,請文件化在每個操作上。
TypeScript 型別產生
產生與 OpenAPI 規格完全相符的型別。當程式碼庫使用品牌型別時,為 ID 使用品牌型別。
// 從 OpenAPI 規格產生——請勿手動編輯
export interface CreateUserRequest {
name: string;
email: string;
role?: 'admin' | 'member' | 'viewer';
}
export interface CreateUserResponse {
id: string;
name: string;
email: string;
role: 'admin' | 'member' | 'viewer';
createdAt: string; // ISO 8601
}
export interface ErrorResponse {
error: {
code: string;
message: string;
details?: Array<{
field: string;
issue: string;
}>;
};
}
export interface PaginatedResponse<T> {
data: T[];
meta: {
page: number;
perPage: number;
total: number;
hasMore: boolean;
};
}
Curl 範例產生
為每個端點產生一個 curl。使用真實但明顯是假的資料。
# GET /api/users/:id -- 取得單一使用者
curl -X GET https://api.example.com/api/users/usr_abc123 \
-H "Authorization: Bearer sk_test_..." \
-H "Accept: application/json"
# POST /api/users -- 建立新使用者
curl -X POST https://api.example.com/api/users \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"name": "Ada Lovelace",
"email": "[email protected]",
"role": "member"
}'
# GET /api/users -- 列出使用者(含分頁)
curl -X GET "https://api.example.com/api/users?page=1&perPage=20&role=admin" \
-H "Authorization: Bearer sk_test_..." \
-H "Accept: application/json"
Curl 範例的規則:
- 始終包含完整 URL(使用
https://api.example.com作為基礎) - 包含所有必要的標頭
- 對於請求主體,使用帶有格式化 JSON 的
-d - 引用包含查詢參數的 URL
- 即使對於 GET 請求也要明確顯示 HTTP 方法
- 對權杖使用
sk_test_...(明顯是假的) - 使用真實的欄位值,而不是 "string" 或 "test"
反模式
1. 文件化理想化的 API
症狀:規格描述了程式碼中尚不存在的端點 修正:只文件化程式碼實際實作的內容。如果計劃中的端點僅存在於註解或規格檔案中,但不在處理器中,則將其排除。
2. 忽略中介軟體的副作用
症狀:文件說端點是公開的,但實際上需要透過上層中介軟體的認證
修正:從應用程式根目錄追蹤到處理器的完整中介軟體鏈。路由定義上方的 router.use(auth) 表示其下的所有路由都需要身份驗證。
3. 假設請求主體形狀
症狀:由於沒有驗證,將 req.body 文件化為 any 或 object
修正:從使用情況推斷(屬性存取、解構、資料庫呼叫)。標記為 x-inferred: true。建議添加 Zod 驗證。
4. 扁平化的錯誤文件
症狀:每個端點列出相同的通用 "400 Bad Request",沒有詳細資訊 修正:閱讀每個處理器的錯誤路徑。文件化回傳的特定錯誤碼和訊息。不同的驗證失敗應顯示不同的範例回應。
5. 過時產生的文件
症狀:規格只產生一次,之後從未隨著程式碼變更而更新
修正:產生到一個已知的路徑(docs/openapi.yaml)。添加一個 CI 檢查,重新產生並與已提交的規格進行比對。如果它們不一致,則建置失敗。
6. 缺少分頁文件
症狀:列表端點文件化時沒有包含分頁的查詢參數結構
修正:如果處理器支援 page、limit、cursor、offset 或類似參數,請使用預設值和最大值來記錄它們。
7. 未文件化的檔案上傳
症狀:Multipart/form-data 端點被文件化為 JSON
修正:偵測 multer、formidable、busboy 或框架原生的檔案處理。使用 multipart/form-data 內容類型與適當的二進位結構。
品質檢查清單
[ ] 程式碼庫中的每個路由處理器都有對應的 OpenAPI 操作
[ ] 路徑參數在程式碼和規格之間匹配(沒有 :id 與 {userId} 的不一致)
[ ] 請求主體結構涵蓋所有必填和選填欄位
[ ] 回應結構與實際的 JSON 結構相符(透過閱讀處理器回傳值驗證)
[ ] 身份驗證需求與中介軟體鏈分析相匹配
[ ] 存在速率限制中介軟體的地方,速率限制資訊已文件化
[ ] 錯誤回應已根據處理器中的實際 throw/return 陳述進行分類
[ ] Curl 範例可在執行中的實例上成功執行
[ ] TypeScript 型別編譯無錯誤
[ ] 沒有留下未文件化的 x-inferred 結構(每個標記的結構都有一個 TODO,用於適當的驗證)
[ ] 分頁參數已文件化,包含預設值和最大值
[ ] 檔案上傳端點使用 multipart/form-data 內容類型
[ ] OpenAPI 規格通過 spectral 或 swagger-cli lint 驗證
[ ] 產生的規格已提交到已知路徑,用於 CI 比對
輸出成品
docs/openapi.yaml—— 完整的 OpenAPI 3.0/3.1 規格docs/api-types.d.ts—— 所有請求/回應形狀的 TypeScript 型別定義docs/api-examples.sh—— 每個端點的可執行 curl 範例docs/api-coverage.md—— 列出已文件化與未文件化路由的報告
驗證
# 檢查產生的 OpenAPI 規格
npx @stoplight/spectral-cli lint docs/openapi.yaml
# 驗證規格結構
npx swagger-cli validate docs/openapi.yaml
# 產生 TypeScript 用戶端以驗證型別可編譯
npx openapi-typescript docs/openapi.yaml -o /tmp/api-check.d.ts
tsc --noEmit /tmp/api-check.d.ts
# 與上次提交的規格進行比對(CI 檢查)
diff <(cat docs/openapi.yaml) <(npx tsx scripts/generate-api-docs.ts --stdout) && echo "規格是最新的" || echo "規格已過時"
涵蓋範圍:OpenAPI 產生 | TypeScript 型別提取 | Curl 範例建立 | 身份驗證文件化 | 速率限制文件化 | 錯誤碼分類
搭配使用:api-architect(先設計,後文件化)| openapi-spec-writer(手動編寫)| test-automation-expert(根據產生的規格進行合約測試)