首页 全部提示词 Web Development

Swagger API 同步

Swagger Api Synchronization

文本 @nurullah 5,133 字符 在 prompts.chat 查看 译文有问题?

翻译时结构校验未通过(标题数不一致:原文 9,译文 4;结构化行数减少:原文 49,译文 27;标题数不一致:原文 9,译文 6;结构化行数减少:原文 49,译文 30),译文可能有格式偏差,建议对照英文原文使用。

中文译文

在本项目中搭建 OpenAPI 规范同步工作流

我想要和我在另一个仓库里用的那套后端规范工作流一样的东西:一个脚本,把线上 OpenAPI 规范 与本地快照做 diff,并生成一份面向前端的变更报告,外加一个 /api-sync 斜杠命令,把该报告转化为分阶段计划。

开始之前先从仓库里把这些填好(只有实在搞不明白时才来问我):

  • 规范来源:自己去找——见第 0 部分。在你找过之前不要向我要 URL。 你找到的那个就作为脚本的默认值,可通过 OPENAPI_URL 环境变量覆盖。
  • 快照路径:api-spec/openapi.yaml · 报告路径:api-spec/CHANGES.md
  • 用于交叉引用的源码根目录:src/(按本仓库的布局调整)
  • 响应校验库:zod(若本仓库用的是别的就调整)
  • 快照进 git 吗? yaml 保持 gitignore(太大/太杂,不适合进历史),但要提交 CHANGES.md——生成的报告是改动内容与时间的持久记录。同时忽略 api-spec/openapi-*.yaml 和 api-spec/CHANGES-*.md(带日期的手动归档)。

先读一遍本仓库(包管理器、脚本约定、API 调用和响应 schema 的写法),并匹配它的风格。不要 凭空编造路径——用 grep 找出真实路径。


第 0 部分 — 在写任何东西之前先找到规范

先做这一步,然后告诉我你找到了什么。不要瞎猜 URL,也不要在搜索一无所获之前 来问我。

1. 文档——最省事的地方,而且通常是对的。 README.md、CLAUDE.md、 AGENTS.md、CONTRIBUTING.md、docs/、.github/、.cursor/rules/ 下的任何文件、 *.http/*.rest 草稿文件,或 wiki 的本地检出。URL 常出现在正文里("API 文档: …/swagger")、搭建步骤里,或后端仓库链接旁边:

grep -rniE 'swagger|openapi|api-?docs|redoc|\/v3\/api-docs' --include='*.md' --include='*.mdx' --include='*.txt' --include='*.http' --include='*.rest' . | grep -v node_modules

两个坑:Swagger UI 链接(…/swagger-ui/index.html、…/docs、…/redoc)是 HTML 页面,不是规范本身——要从它推导出机器可用的 URL(/swagger-ui/index.html → /v3/api-docs,/docs → /openapi.json,/redoc → 其 HTML 中的 spec-url),并用 curl 验证。另外,文档 URL 可能已过时——采用之前先确认它能正常响应,如果 README 指向的地方已经失效,要告诉我。

2. 仓库内或仓库附近已有的规范文件——通常有人已经 vendor 了一份:

find . -path ./node_modules -prune -o -iregex '.*\(swagger\|openapi\|api-docs\).*\.\(ya?ml\|json\)' -print
git ls-files | grep -iE 'swagger|openapi|api-docs'

另外还要检查 node_modules/.cache/、.next/cache/、dist/、build/、coverage/,以及任何 被 gitignore 忽略的 api/、api-spec/、docs/、schemas/ 文件夹——之前的某次 codegen 运行常常在那里留下 一份副本。过期的缓存副本依然有用:它是用来初始化快照的基线, 这样第一次真正的 diff 才有意义,而不是“一切都是新的”。如果找到一份,先说明它有多 旧(git log -1 / 文件 mtime),然后再决定是否信任它。

3. 已经写明来源的生成器配置——这是信号最强的命中结果,因为 它指向的正是团队实际使用的那个 URL 或路径:

grep -rniE 'openapi|swagger|api-docs' --include='*.json' --include='*.ts' --include='*.js' --include='*.mjs' --include='*.yaml' --include='*.yml' --include='.env*' --include='Makefile' --include='*.sh' -l . | grep -v node_modules

专门查找:openapi-typescript / orval.config.* / kubb.config.* / swagger-typescript-api / @hey-api/openapi-ts 的配置、package.json 中名称含 openapi 的 npm 脚本、 .env* 里的 API 基础 URL、docker-compose.yml 中的服务 URL、CI 工作流步骤,或者 已提交入库的生成客户端——其头部注释注明了所依据的源规范。

4. 从 API 基础 URL 推导。 如果你只找到了基础 URL,在问我之前,先探测该后端所用框架的约定路径—— FastAPI /openapi.json、Spring/springdoc /v3/api-docs(+ .yaml)、ASP.NET /swagger/v1/swagger.json、NestJS /api-json、Rails/rswag /api-docs/v1/swagger.yaml,再加上通用的 /openapi.yaml 和 /swagger.json:

curl -sS -o /dev/null -w '%{http_code} %{content_type} %{url_effective}\n' <BASE>/openapi.json

报告哪些有响应。如果它们全都需要认证,就直说——绝不要把令牌硬编码进脚本。

5. 全都不管用? 那就来问我,并告诉我你排除了哪些可能。

如果规范无法通过 HTTP 访问

不要强行采用抓取式设计。把来源做成单一的 SPEC_SOURCE,它可以是 URL、本地 路径或 shell 命令(例如后端仓库自己的 make openapi,或同级检出目录中 生成的文件),按该顺序解析:--to <file> 标志 → OPENAPI_URL 环境变量 → 你发现的 默认值。下游的一切——diff、报告、快照——都保持不变。在 CLAUDE.md 中说明 该仓库用的是哪一种,以及如何刷新它。

第 1 部分 — scripts/sync-api.mjs

一个依赖极少的单一 Node ESM 脚本(js-yaml 是唯一的新依赖;使用该仓库的包 管理器)。标志:

node scripts/sync-api.mjs               fetch remote → diff vs snapshot → write report + overwrite snapshot
node scripts/sync-api.mjs --check       diff only, snapshot untouched, exit 1 if it drifted (CI-friendly)
node scripts/sync-api.mjs --from <file> diff against <file> instead of the snapshot
node scripts/sync-api.mjs --to <file>   treat <file> as "remote" instead of fetching (offline)
node scripts/sync-api.mjs --json        also print the raw diff as JSON on stdout
  1. 将每一处变更与实际代码交叉核对。 用以下命令一次性加载所有源文件 git ls-files --cached --others --exclude-standard <src root>(包含未跟踪文件,这样本会话 中新增的调用点也会被计入;跳过已列出但已从工作树中删除的文件),然后:
    • 某个路径的调用点:将路径参数转换为单段通配符,并要求匹配 以引号/反引号/? 结尾,这样 /orgs/{id} 就不会匹配到 /orgs/${id}/archive。 每个被移除/变更的操作都要列出其调用点,或标注 "⬜ 无调用点"。
    • Schema 镜像:找到存放着我们对某个 spec schema 的响应校验镜像的文件 — 在任意位置匹配 fooBarSchema,而裸的 PascalCase 名称仅在 schemas.ts 内匹配 (否则在其他地方会与无关的 TS 标识符冲突)。命名约定需适配 该仓库实际使用的约定 — 先 grep。
    • 若新操作的路径已在 src/ 中被引用,则添加 "⚠️ 路径已被 引用 — 请检查方法" 的备注。

报告格式(api-spec/CHANGES.md)

文件头包含生成日期、基线标签、spec 的 info.version,以及操作和 schema 数量的变更前→变更后对比;然后是一个简短的新增/移除/变更表格;然后按以下顺序分节:

  • ## 🔴 Removed operations — breaking if we call them(附调用点)
  • ## 🟠 Changed operations(每处变更用嵌套列表项 + 调用点)
  • ## 🟢 New operations,按路径区域分组(第一段,并针对 该 API 的前缀做合理的特例处理)— 摘要、响应 schema、请求体 schema
  • ## 🔴 Removed schemas(附镜像文件)
  • ## 🟠 Changed schemas — 有镜像的条目排在最前,并连同其镜像文件一起加粗,因为 它们才是当前可能破坏解析的部分;其余仅供参考
  • ## 🟢 New schemas(单行,逗号分隔)

第 3 部分 — 接入

  • 添加 gitignore 条目。
  • 在 CLAUDE.md 中添加一个简短的 后端变更工作流 章节(如果没有就创建):快照 仅存于本地且被 gitignore 忽略,CHANGES.md 是提交入库的记录,CHANGES.md 是自动 生成的,绝不手工编辑它;后端开发说有东西上线时就运行 /api-sync;用 npm run sync:api -- --check 检测漂移;并养成习惯:在进行大的后端变更之前,先归档一份带日期的 api-spec/openapi-YYYY-MM-DD.yaml 作为已提交入库的参考基准点。
  • 运行一次脚本来生成初始快照,并把第一份报告给我看 — 另外附上一行说明,注明 spec 的来源, 如果你用缓存副本做的初始化,还要说明它有多陈旧。

English original

Set up an OpenAPI spec-sync workflow in this project

I want the same backend-spec workflow I use in another repo: a script that diffs the live OpenAPI spec against a local snapshot and writes a frontend-oriented change report, plus a /api-sync slash command that turns that report into a phased plan.

Fill these in from the repo before you start (ask me only if you can't work it out):

  • Spec source: find it yourself — see Part 0. Don't ask me for the URL until you've looked. Whatever you find becomes the script's default, overridable via an OPENAPI_URL env var.
  • Snapshot path: api-spec/openapi.yaml · Report path: api-spec/CHANGES.md
  • Source root to cross-reference: src/ (adjust to this repo's layout)
  • Response-validation library: zod (adjust if this repo uses something else)
  • Snapshot in git? Keep the yaml gitignored (too large/noisy for history) but commit CHANGES.md — the generated report is the durable record of what changed and when. Also ignore api-spec/openapi-*.yaml and api-spec/CHANGES-*.md (dated manual archives).

Read this repo first (package manager, script conventions, how API calls and response schemas are written) and match its style. Don't invent paths — grep for the real ones.


Part 0 — find the spec before you write anything

Do this first and tell me what you found. Don't guess a URL, and don't ask me until this comes up empty.

1. The docs — cheapest place, and usually right. README.md, CLAUDE.md, AGENTS.md, CONTRIBUTING.md, anything under docs/, .github/, .cursor/rules/, a *.http/*.rest scratch file, or a wiki checkout. The URL is often in prose ("API docs: …/swagger"), in a setup step, or next to the backend repo link:

grep -rniE 'swagger|openapi|api-?docs|redoc|\/v3\/api-docs' --include='*.md' --include='*.mdx' --include='*.txt' --include='*.http' --include='*.rest' . | grep -v node_modules

Two gotchas: a Swagger UI link (…/swagger-ui/index.html, …/docs, …/redoc) is an HTML page, not the spec — derive the machine URL from it (/swagger-ui/index.html → /v3/api-docs, /docs → /openapi.json, /redoc → the spec-url in its HTML) and verify with curl. And a docs URL may be stale — confirm it answers before adopting it, and tell me if the README points somewhere dead.

2. A spec file already in or near the repo — someone usually vendored one:

find . -path ./node_modules -prune -o -iregex '.*\(swagger\|openapi\|api-docs\).*\.\(ya?ml\|json\)' -print
git ls-files | grep -iE 'swagger|openapi|api-docs'

Also check node_modules/.cache/, .next/cache/, dist/, build/, coverage/ and any gitignored api/, api-spec/, docs/, schemas/ folder — a previous codegen run often left a copy there. A stale cached copy is still useful: it's a baseline to seed the snapshot with, so the first real diff is meaningful instead of "everything is new". If you find one, say how old it is (git log -1 / file mtime) before deciding to trust it.

3. A generator config that already names the source — this is the highest-signal hit, because it points at whatever URL or path the team actually uses:

grep -rniE 'openapi|swagger|api-docs' --include='*.json' --include='*.ts' --include='*.js' --include='*.mjs' --include='*.yaml' --include='*.yml' --include='.env*' --include='Makefile' --include='*.sh' -l . | grep -v node_modules

Look specifically for: openapi-typescript / orval.config.* / kubb.config.* / swagger-typescript-api / @hey-api/openapi-ts config, an openapi-ish npm script in package.json, a .env* API base URL, docker-compose.yml service URLs, CI workflow steps, or a committed generated client whose header comment cites its source spec.

4. Derive it from the API base URL. If you only find a base URL, probe the conventional paths for that backend's framework before asking me — FastAPI /openapi.json, Spring/springdoc /v3/api-docs (+ .yaml), ASP.NET /swagger/v1/swagger.json, NestJS /api-json, Rails/rswag /api-docs/v1/swagger.yaml, plus plain /openapi.yaml and /swagger.json:

curl -sS -o /dev/null -w '%{http_code} %{content_type} %{url_effective}\n' <BASE>/openapi.json

Report which ones answered. If they all need auth, say so — don't bake a token into the script.

5. Nothing works? Then ask me, and tell me what you ruled out.

If the spec isn't reachable over HTTP

Don't force the fetch design. Make the source a single SPEC_SOURCE that may be a URL, a local path, or a shell command (e.g. the backend repo's own make openapi, or a sibling checkout's generated file), resolved in that order: --to <file> flag → OPENAPI_URL env → the default you discovered. Everything downstream — diff, report, snapshot — is unchanged. Say in CLAUDE.md which one this repo uses and how to refresh it.

Part 1 — scripts/sync-api.mjs

A single dependency-light Node ESM script (js-yaml is the only new dep; use the repo's package manager). Flags:

node scripts/sync-api.mjs               fetch remote → diff vs snapshot → write report + overwrite snapshot
node scripts/sync-api.mjs --check       diff only, snapshot untouched, exit 1 if it drifted (CI-friendly)
node scripts/sync-api.mjs --from <file> diff against <file> instead of the snapshot
node scripts/sync-api.mjs --to <file>   treat <file> as "remote" instead of fetching (offline)
node scripts/sync-api.mjs --json        also print the raw diff as JSON on stdout

Add "sync:api": "node scripts/sync-api.mjs" to package.json.

If no snapshot exists yet: write the fetched spec as the snapshot, print "seeded — re-run after the backend ships to see a diff", exit 0. Never report the whole API as "new". Exception: if Part 0 turned up an older cached/vendored spec, seed the snapshot from that instead and run a real diff against the live spec on the first run — tell me the cached copy's date so I know what the baseline means.

What it must diff

Flatten paths into a "GET /a/b" → operation map and diff operations and components.schemas separately:

Operations

  • added / removed / changed
  • params: new ones (flag required), required↔optional flips, enum values added/removed — key a param by in:name (or ref:Name for $ref params), not by array index
  • request body and success-response schema: if the $ref name changed, report the rename; if the shape is inline (no $ref), diff its properties here — an unnamed schema is diffed here or nowhere. Resolve allOf: [$ref] wrappers to the underlying name, and render oneOf/anyOf unions as A | B (gaining/losing a union member is a real behavioural change).
  • new/removed non-2xx status codes
  • security requirement changes, newly deprecated

Schemas

  • properties added (mark required) / removed / retyped
  • enum values added or removed (on the schema and on each property, including items.enum)
  • required↔optional flips

The two things that make this report worth having

  1. error_code extraction from response prose. Machine error codes are usually documented nowhere but each non-2xx response's description text ("… already an active member (already_member)"), so a new branch we need to handle looks like nothing changed to a schema-level diff. Parse them by context, not vocabulary: tokens inside (...), tokens after error_code-ish prose, and any snake_case token that appears in some schema's literal error_code enum. Do not filter out tokens that collide with field names or enum values — those collisions are exactly the codes that matter most. Drop tokens introduced by "field X" phrasing (those are field names, not codes). Report added codes, and for a code that stopped being documented, grep the source for "that_code" and say which file branches on it — that's a dead branch.

  2. Cross-reference every change against the actual code. Load every source file once via git ls-files --cached --others --exclude-standard <src root> (include untracked so a call site added this session counts; skip files listed but deleted from the working tree), then:

    • Call sites for a path: turn path params into single-segment wildcards and require the match to end at a quote/backtick/? so /orgs/{id} doesn't match /orgs/${id}/archive. Every removed/changed operation lists its call sites, or "⬜ no call site".
    • Schema mirrors: find the file holding our response-validation mirror of a spec schema — match fooBarSchema anywhere, plus the bare PascalCase name only inside a schemas.ts (elsewhere it collides with unrelated TS identifiers). Adapt the naming convention to whatever this repo actually uses — grep first.
    • A new operation whose path is already referenced in src/ gets a "⚠️ path already referenced — check the method" note.

Report format (api-spec/CHANGES.md)

Header with generation date, baseline label, spec info.version, and before→after operation and schema counts; then a small added/removed/changed table; then sections, in this order:

  • ## 🔴 Removed operations — breaking if we call them (with call sites)
  • ## 🟠 Changed operations (nested bullets per change + call sites)
  • ## 🟢 New operations, grouped by path area (first segment, with sensible special cases for this API's prefixes) — summary, response schema, request-body schema
  • ## 🔴 Removed schemas (with mirror files)
  • ## 🟠 Changed schemas — mirrored ones sorted first and bolded with the mirror file, since those are what can break parsing today; the rest are informational
  • ## 🟢 New schemas (one comma-separated line)

If nothing changed, the body is exactly "No changes since the last snapshot." Top the file with "do not edit by hand".

Keep the script commented where a decision is non-obvious (the error_code heuristic, the path matcher's end anchor, why untracked files are included) — future-me reads those.

Part 2 — .claude/commands/api-sync.md

A slash command (/api-sync [scope], scope optional, also accepts implement) that runs the workflow. Frontmatter: description + argument-hint. Steps:

  1. Diff — run npm run sync:api, read api-spec/CHANGES.md. Call out the two judgement calls the report can flag but not decide: a changed request body on a live call site is actionable even with no schema mirror (we build bodies by hand), and a new error_code is a branch we don't have yet — if it's a field error it must render inline on the field, not just as a toast. If the report says no changes, say so and stop — don't invent work.
  2. Classify every item into: Breaking (P0) · Silently wrong — a mirrored schema gained a required field or an enum grew values our validator rejects (P0) · Now-incomplete — a hand-built request body gained a field, or a new error_code we don't branch on (P1) · Un-mocks a screen (P1) · Extends a screen (P2) · Net-new feature (P3) · Backend-only (drop). Never skip an item; if it fits nowhere, list it as an open question. Verify each classification against the code rather than assuming — open the named mirror file, grep for the mock fixture, confirm the screen exists.
  3. Write the plan — a dated section in ROADMAP.md (or this repo's equivalent; create one if there's none), ordered by those priorities and phased so each phase ships independently. Per item: the endpoints and files that change, what the user can do afterwards that they can't today (the point of the work — not "wire endpoint X"), and whether it's blocked and on whom. One line per item. Then update whatever coverage/tracker docs this repo keeps.
  4. Report back in chat — what the backend shipped in one plain-language paragraph, anything broken right now with the file to fix, the phases one line each, and genuine questions for the backend dev only. Stop there; only if the argument contains implement, build Phase 1 only, then run this repo's typecheck + lint and report before continuing.

Part 3 — wire it in

  • Add the gitignore entries.
  • Add a short Backend-change workflow section to CLAUDE.md (create it if missing): the snapshot is local and gitignored, CHANGES.md is the committed record, CHANGES.md is generated so never hand-edit it, /api-sync when the backend dev says something shipped, npm run sync:api -- --check to detect drift, and the habit of archiving a dated api-spec/openapi-YYYY-MM-DD.yaml before a big backend change as a committed reference point.
  • Seed the snapshot by running the script once, and show me the first report — plus a one-line note on where the spec came from and, if you seeded from a cached copy, how stale it was.