Use Cases (更新: 2026/6/3)

Claude Code OpenAPI 3.1 实战:Swagger 校验与 TypeScript 生成

用 Claude Code 编写 OpenAPI 3.1,完成 Swagger 校验并生成可靠的 TypeScript 客户端。

Claude Code OpenAPI 3.1 实战:Swagger 校验与 TypeScript 生成

让 Claude Code 写 OpenAPI 文件很快,但直接相信它很危险。它可能根据页面文案推断字段,添加数据库里不存在的 enum 值,或者让每个接口返回不同的错误结构。OpenAPI 一旦变成“看起来很完整的假合同”,前端、移动端和合作方都会被拖累。

本文把 Claude Code 当作 API 合同维护助手,而不是自动生成真理的工具。我们会创建 OpenAPI 3.1 规格,执行校验,生成 TypeScript 客户端和服务端 stub,并加入一个小脚本来拦截常见的 AI 幻觉 schema。OpenAPI 用机器可读的方式描述 REST 接口、请求、响应、认证和错误;Swagger 则常指用于编辑、预览和检查这些文档的工具生态。

Masa 在一个小型订单 API 上测试过这个流程。第一次只说“读取仓库并生成 OpenAPI”,Claude Code 生成了听起来合理但代码里不存在的 cancelled 状态。更稳定的做法是:指定必须读取的文件,禁止未验证字段,运行校验命令,并把不确定点放到公开 schema 之外。

请优先使用官方资料。2026 年 6 月 3 日确认时,OpenAPI Specification latest 已是 3.2.0,但本文为了生成工具兼容性把示例固定在 openapi: 3.1.0。3.1 细节请看 OpenAPI Specification 3.1.2,预览和编辑看 Swagger Editor documentation,代码生成看 OpenAPI Generator usagetypescript-fetch generatortypescript-nestjs-server generator。处理 OpenAPI 3.1 时,请确认当前 Swagger 文档中 Swagger Editor Next 的支持情况。

相关流程可继续阅读:Claude Code API 开发指南API 测试指南安全最佳实践CLAUDE.md 最佳实践

flowchart LR
  A["实现、数据库、测试"] --> B["Claude Code 起草 OpenAPI"]
  B --> C["Swagger 与 CLI 校验"]
  C --> D["生成 TypeScript client"]
  C --> E["合同测试"]
  C --> F["安全评审"]
  D --> G["前端或合作方联调"]
  E --> H["CI 发布门禁"]
  F --> H

真实使用场景

场景交给 Claude Code人来决定
为已有 API 补规格盘点路由、模型、状态码、认证边界哪些字段公开、兼容性、命名规范
合同优先开发OpenAPI 3.1 草案、示例、生成命令业务规则、错误策略、发布范围
前端联调TypeScript client、调用示例、类型差异交互体验、重试策略、提示文案
合同测试检查 operationId、4xx 响应、enum、认证声明破坏性变更策略和例外批准
安全评审列出公开 schema、认证方式、PII 候选、内部 ID法务风险、审计证据、合作方承诺
合作方 API 文档Swagger 可读说明、示例、认证说明合同条款、限流、支持范围

关键原则是:代码、数据库、测试或产品决策无法证明的字段,不应该进入公开 schema。

可复制的最小项目

需要 Node.js 20+。OpenAPI Generator 生成代码时使用 Java,因此本地执行生成命令还需要 Java。

mkdir openapi-claude-demo
cd openapi-claude-demo
npm init -y
npm install -D @openapitools/openapi-generator-cli js-yaml
mkdir specs generated scripts

package.json

{
  "type": "module",
  "scripts": {
    "validate:openapi": "openapi-generator-cli validate -i specs/openapi.yaml",
    "lint:contract": "node scripts/check-openapi-rules.mjs",
    "test:contract": "node --test scripts/contract.test.mjs",
    "generate:client": "openapi-generator-cli generate -i specs/openapi.yaml -g typescript-fetch -o generated/client --additional-properties=supportsES6=true,npmName=@example/orders-client,npmVersion=0.1.0",
    "generate:server": "openapi-generator-cli generate -i specs/openapi.yaml -g typescript-nestjs-server -o generated/server",
    "contract": "npm run validate:openapi && npm run lint:contract && npm run test:contract && npm run generate:client"
  },
  "devDependencies": {
    "@openapitools/openapi-generator-cli": "latest",
    "js-yaml": "latest"
  }
}

specs/openapi.yaml

openapi: 3.1.0
info:
  title: Orders API
  version: 0.1.0
  description: Contract-first sample API for order intake.
servers:
  - url: https://api.example.com/v1
paths:
  /orders:
    post:
      operationId: createOrder
      summary: Create an order
      tags: [orders]
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateOrderRequest"
      responses:
        "201":
          description: Order created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Order"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
  responses:
    BadRequest:
      description: Invalid request
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    Unauthorized:
      description: Missing or invalid token
  schemas:
    CreateOrderRequest:
      type: object
      additionalProperties: false
      required: [customerEmail, items]
      properties:
        customerEmail:
          type: string
          format: email
        note:
          type: [string, "null"]
        items:
          type: array
          minItems: 1
          items:
            $ref: "#/components/schemas/OrderItemInput"
    OrderItemInput:
      type: object
      additionalProperties: false
      required: [sku, quantity]
      properties:
        sku:
          type: string
        quantity:
          type: integer
          minimum: 1
    Order:
      type: object
      additionalProperties: false
      required: [id, customerEmail, status, items, createdAt]
      properties:
        id:
          type: string
          format: uuid
        customerEmail:
          type: string
          format: email
        status:
          type: string
          enum: [pending, paid]
        items:
          type: array
          items:
            $ref: "#/components/schemas/OrderItemInput"
        createdAt:
          type: string
          format: date-time
    ErrorResponse:
      type: object
      additionalProperties: false
      required: [code, message]
      properties:
        code:
          type: string
          enum: [invalid_request, unauthorized]
        message:
          type: string

添加本地合同检查:

import { readFileSync } from "node:fs";

const spec = readFileSync("specs/openapi.yaml", "utf8");
const forbidden = [
  { pattern: /\bTBD\b|\bTODO\b|placeholder/i, reason: "unfinished placeholder" },
  { pattern: /nullable:\s*true/, reason: "OpenAPI 3.1 should use JSON Schema null types" },
  { pattern: /password|secret|clientSecret|apiKey/i, reason: "review sensitive fields before publishing" }
];

const errors = forbidden.filter((rule) => rule.pattern.test(spec)).map((rule) => `- ${rule.reason}`);
if (!spec.includes("additionalProperties: false")) errors.push("- schemas should explicitly decide additionalProperties");
if (errors.length > 0) {
  console.error("Contract review failed:\n" + errors.join("\n"));
  process.exit(1);
}
console.log("Contract review passed.");

再加一个可以放进 CI 的合同测试。它不替代集成测试,而是检查公开合同本身:operationId 是否稳定、更新操作是否声明认证、4xx 是否复用公共响应、Order.status 是否仍然只包含当前实现支持的值。

// scripts/contract.test.mjs
import { readFileSync } from "node:fs";
import assert from "node:assert/strict";
import test from "node:test";
import { load } from "js-yaml";

const doc = load(readFileSync("specs/openapi.yaml", "utf8"));
const httpMethods = new Set(["get", "put", "post", "delete", "patch", "options", "head", "trace"]);

function operations() {
  return Object.entries(doc.paths ?? {}).flatMap(([path, pathItem]) =>
    Object.entries(pathItem)
      .filter(([method]) => httpMethods.has(method))
      .map(([method, operation]) => ({ path, method, operation }))
  );
}

test("operationId is unique and camelCase", () => {
  const ids = operations().map(({ operation }) => operation.operationId);
  assert.equal(new Set(ids).size, ids.length);
  for (const id of ids) {
    assert.match(id, /^[a-z][A-Za-z0-9]*$/);
  }
});

test("mutating operations require security", () => {
  for (const { path, method, operation } of operations()) {
    if (["get", "head", "options"].includes(method)) continue;
    assert.ok(operation.security?.length, `${method.toUpperCase()} ${path} must declare security`);
  }
});

test("client-visible 4xx responses reuse shared components", () => {
  for (const { path, method, operation } of operations()) {
    for (const [status, response] of Object.entries(operation.responses ?? {})) {
      if (!status.startsWith("4")) continue;
      assert.ok(response.$ref?.startsWith("#/components/responses/"), `${method.toUpperCase()} ${path} ${status}`);
    }
  }
});

test("Order status enum matches the current implementation decision", () => {
  const status = doc.components.schemas.Order.properties.status;
  assert.deepEqual(status.enum, ["pending", "paid"]);
});

执行:

npm run validate:openapi
npm run lint:contract
npm run test:contract
npm run generate:client
npm run generate:server

Claude Code 提示词

请为订单 API 创建 OpenAPI 3.1 合同。
先读取 src/routes/orders.ts、src/domain/order.ts、prisma/schema.prisma 和 tests/orders.test.ts。
不要编造代码、schema、测试或产品说明无法证明的字段、enum 或错误码。
不确定点写入 x-claude-review,不要写进公开 schemas。
不要使用 nullable: true。operationId 使用稳定的 camelCase。
受保护操作必须声明 security。内部字段、PII、secret 候选先放进 x-claude-review,不要直接发布。
运行 npm run validate:openapi、npm run lint:contract 和 npm run test:contract,并修复失败。
返回 specs/openapi.yaml diff、TypeScript client 调用示例、安全评审备注和需要人工确认的问题。

合同测试与安全评审

对 SaaS 或合作方 API 来说,OpenAPI 评审不是简单校对文字。建议拆成四条线:规格评审、生成 client 评审、合同测试和安全评审。规格评审看 path、method、schema、状态码和 example;生成 client 评审看 typescript-fetch 产生的类型和 null/optional 是否好用;合同测试负责拦截认证漏写、错误结构漂移和 enum 变更;安全评审负责公开字段、PII、内部 ID、server URL 和 secret 候选。

Claude Code 可以整理差异、生成测试草案、准备评审表。人仍然要决定哪些字段可以公开、限流和 SLA 怎么承诺、审计证据要保留多久。API 集成负责人可以把“生成 client 调用一次、合同测试通过、security 与公开 schema 已确认”写进 PR 完成条件。

常见失败

第一是幻觉 schema,例如 cancelledrefundedadminNote 看起来合理但没有实现。第二是错误结构不一致。第三是在 3.1 文件里使用 OpenAPI 3.0 的 nullable: true。第四是手改生成出来的 client 或 server stub。第五是只看 Swagger 预览,不检查 operationId、认证、示例和 enum 兼容性。

团队评审清单

在团队里落地时,建议把 OpenAPI 评审拆成四步。第一步让后端确认 schema 是否来自真实代码和数据库,不接受“以后可能会有”的字段。第二步让前端用生成的 TypeScript client 写一次实际调用,检查分页、空值、错误分支是否好用。第三步让 QA 把 400、401、500 等失败路径补进 API 测试,避免只验证 200 成功路径。第四步让产品或对外接口负责人确认哪些字段可以公开,哪些只是内部实现。

这个清单看起来比直接生成 YAML 慢,但它能减少后续沟通成本。尤其是合作方 API,一旦外部客户按照错误 schema 开发,修正成本会比现在多很多。Claude Code 适合整理差异、生成候选 diff、补充示例,但最终发布前必须有人确认业务含义。

培训与咨询 CTA

OpenAPI 整理很适合做团队培训或咨询,因为交付物明确:规格文件、TypeScript 客户端、服务端 stub、合同测试、验证命令和 CI 清单。个人可以从 ClaudeCodeLab 产品与模板 开始,复用提示词和评审表。团队如果要结合真实仓库整理 CLAUDE.md、API 测试和安全评审,可以使用 Claude Code 培训与咨询

实测结果

Masa 的测试中,自由提示词会加入未实现状态并改变错误格式。加入必读文件、禁止编造、openapi-generator-cli validate、本地检查和 node --test 合同测试后,人工审核主要集中在四个问题:哪些字段可公开、enum 是否需要预留扩展、认证错误文案怎么写、是否有内部信息进入 schema。OpenAPI 最稳定的用法不是把文档外包给 AI,而是让 AI 和团队围绕同一份合同工作。

#Claude Code #OpenAPI #Swagger #API design #REST
免费

免费 PDF: Claude Code 速查表

输入邮箱即可获取一页 PDF,整理常用命令、审查习惯和安全工作流。

我们会妥善保护你的信息,不发送垃圾邮件。

把 Claude Code 变成真正能带来结果的工作流

先领取中文说明的免费 PDF,再进入英文商品页选择合适的教材。如果你需要团队落地、流程设计或内容变现支持,也可以直接咨询。

Masa

关于作者

Masa

专注 Claude Code 实务流程、团队导入和内容转化的工程师。