Claude Code OpenAPI 3.1 Guide: Swagger Validation and TypeScript Generation
Build OpenAPI 3.1 specs with Claude Code, validate with Swagger tooling, and generate TypeScript clients.
Asking Claude Code to write an OpenAPI file is fast. Trusting that file without checks is where teams get hurt. The model may infer fields from UI labels, invent enum values that do not exist in the database, or change the error envelope from one endpoint to the next.
This guide treats Claude Code as a contract maintenance assistant, not a magic schema author. You will create an OpenAPI 3.1 spec, validate it, generate a TypeScript client and server stub, and add a small review script that catches the most common AI-generated contract mistakes. OpenAPI describes REST endpoints, request bodies, response bodies, authentication, and errors in a machine-readable format. Swagger is the ecosystem many teams use to edit, preview, and validate those OpenAPI documents.
Masa tested this on a small order API. The first prompt, “read the repo and make OpenAPI,” produced a plausible but wrong cancelled status and a response field that did not exist in the domain model. The better workflow was stricter: tell Claude Code which files to read, forbid unverified fields, run validation, and list unresolved questions outside the public schema.
Keep official references close while working. As of June 3, 2026, the latest OpenAPI Specification is 3.2.0, but this guide pins examples to openapi: 3.1.0 for generator compatibility. Use the OpenAPI Specification 3.1.2, Swagger Editor documentation, OpenAPI Generator usage, typescript-fetch generator, and typescript-nestjs-server generator as the baseline. For OpenAPI 3.1 previews, check Swagger Editor Next support in the current Swagger docs before assuming an older editor handles every 3.1 feature.
For the wider workflow, pair this with the production API development guide, API testing guide, security best practices, and CLAUDE.md best practices.
flowchart LR
A["Implementation, DB, tests"] --> B["Claude Code drafts OpenAPI"]
B --> C["Swagger and CLI validation"]
C --> D["TypeScript client generation"]
C --> E["Contract tests"]
C --> F["Security review"]
D --> G["Frontend or partner integration"]
E --> H["CI publish gate"]
F --> H
Where This Pays Off
Claude Code is most useful when OpenAPI becomes the shared contract between backend, frontend, tests, and partner documentation.
| Use case | Let Claude Code do | Keep human-owned |
|---|---|---|
| Document an existing API | Inventory routes, models, status codes, auth boundaries | Public fields, compatibility, naming policy |
| Contract-first new API | Draft OpenAPI 3.1, examples, generator commands | Business rules, error policy, release scope |
| Frontend integration | Generate TypeScript client, sample calls, type diffs | UX, retries, user-facing messages |
| Contract testing | Check operationIds, 4xx responses, enums, and auth declarations | Breaking-change policy and exception approval |
| Security review | List public schemas, auth schemes, PII candidates, internal identifiers | Legal exposure, audit evidence, partner promises |
| Partner API publishing | Swagger-readable descriptions, examples, auth notes | Contracts, rate limits, support promises |
The important line is simple: if the implementation, database, tests, or product decision do not prove a schema detail, Claude Code should not publish it.
Copy-Paste Setup
The example below is a small order API contract. It runs on Node.js 20 or newer. OpenAPI Generator uses Java, so install Java if you want to run the generation commands locally.
mkdir openapi-claude-demo
cd openapi-claude-demo
npm init -y
npm install -D @openapitools/openapi-generator-cli js-yaml
mkdir specs generated scripts
Update package.json with these scripts:
{
"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"
}
}
Create 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"
examples:
minimum:
value:
customerEmail: buyer@example.com
items:
- sku: SKU-001
quantity: 2
responses:
"201":
description: Order created
content:
application/json:
schema:
$ref: "#/components/schemas/Order"
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/Unauthorized"
get:
operationId: listOrders
summary: List orders
tags: [orders]
parameters:
- name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 100
default: 20
responses:
"200":
description: Orders list
content:
application/json:
schema:
type: object
required: [data]
properties:
data:
type: array
items:
$ref: "#/components/schemas/Order"
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"]
maxLength: 500
items:
type: array
minItems: 1
items:
$ref: "#/components/schemas/OrderItemInput"
OrderItemInput:
type: object
additionalProperties: false
required: [sku, quantity]
properties:
sku:
type: string
minLength: 1
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]
note:
type: [string, "null"]
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
OpenAPI validation catches specification errors. It does not catch every operational risk, so add a tiny rule script:
// scripts/check-openapi-rules.mjs
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.");
Add a small contract test that runs in CI. It checks the public contract itself: stable operation IDs, security on mutating operations, shared 4xx responses, and the current Order.status enum.
// 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"]);
});
Run the workflow:
npm run validate:openapi
npm run lint:contract
npm run test:contract
npm run generate:client
npm run generate:server
Generated clients and server stubs are review artifacts, not the source of truth. Fix specs/openapi.yaml, then regenerate.
Prompt Claude Code With Guardrails
Use a prompt that forces evidence-based schema work:
Create an OpenAPI 3.1 contract for the order API in this repository.
Read these files first:
- src/routes/orders.ts
- src/domain/order.ts
- prisma/schema.prisma
- tests/orders.test.ts
Rules:
- Do not invent fields, enum values, or error codes that are not proven by code, schema, tests, or a product note.
- Put unresolved questions in x-claude-review instead of public schemas.
- Use OpenAPI 3.1. Do not use nullable: true.
- Make every operationId stable camelCase.
- Match the existing 400/401/500 error envelope.
- Declare security on protected operations. Put internal fields, PII, and secret candidates in x-claude-review before public schemas.
- Run npm run validate:openapi, npm run lint:contract, and npm run test:contract, then fix failures.
Return:
- the specs/openapi.yaml diff
- a TypeScript client call example
- security review notes and open questions for human review
The key rule is “do not invent.” Claude Code is useful because it fills gaps, but an API contract is exactly where unverified gap-filling becomes expensive.
Contract Tests And Security Review
For SaaS and partner APIs, OpenAPI review is not just proofreading. Split it into spec review, generated client review, contract tests, and security review so each lead knows what they own.
| Review lane | What to inspect | Stop the release when |
|---|---|---|
| Spec review | paths, methods, schemas, status codes, examples | enums are unimplemented, objects are vague, errors are descriptive only |
| Client generation | typescript-fetch types, call functions, optional/null handling | operationIds change or generated types break consumers |
| Contract tests | 4xx response reuse, auth declarations, enums, public fields | auth is missing, error shape drifts, partner-facing fields change |
| Security review | securitySchemes, PII, internal IDs, secret candidates, server URLs | admin-only fields, internal hosts, or literal secrets reach the public spec |
Claude Code can inventory diffs, draft tests, and build the review table. Humans still own publication decisions, rate limits, contractual commitments, and data exposure.
Failure Cases To Watch
The first pitfall is hallucinated schemas: cancelled, refunded, adminNote, or internalToken appear because they sound reasonable. Reject anything not backed by implementation or product decision.
The second is inconsistent errors. If one endpoint returns { message } and another returns { error: { code } }, generated clients and frontend handling become messy. Define ErrorResponse once and reuse it.
The third is OpenAPI 3.0 habit inside a 3.1 file. In 3.1, prefer JSON Schema null types such as type: [string, "null"] over nullable: true.
The fourth is editing generated code. Generated TypeScript should be disposable. The contract is the source of truth.
The fifth is visual-only review. Swagger UI can look clean while duplicate operationId values, missing auth, weak examples, or incompatible enum changes remain.
Monetization Angle
OpenAPI cleanup is a strong training and consulting offer because the deliverables are concrete: a spec, generated client, server stub, contract test, validation command, and CI checklist. Individual builders can start with ClaudeCodeLab products for reusable prompts and review templates. Teams that want repository-specific rollout, CLAUDE.md, API testing, and security review can use Claude Code training and consultation.
Hands-On Verification
When Masa tested this workflow on the order API, the unrestricted prompt added unimplemented statuses and changed the error shape. The guarded prompt, plus openapi-generator-cli validate, the custom rule script, and node --test contract checks, reduced review to four human decisions: which fields are public, whether enum expansion should be future-proofed, what wording belongs in auth errors, and whether internal data leaked into the schema. The practical lesson was clear: OpenAPI works best as shared contract infrastructure, not as a document to outsource blindly.
Summary
Claude Code can make OpenAPI/Swagger work faster, but only when validation, contract tests, and security review are built into the workflow. Start with OpenAPI 3.1, verify against official docs, preview with Swagger tooling, generate TypeScript clients and server stubs with OpenAPI Generator, and always fix the spec before regenerating artifacts.
Free PDF: Claude Code Cheatsheet
Enter your email and download the one-page Claude Code cheatsheet for commands, review habits, and safe workflows.
We handle your data with care and never send spam.
Level up your Claude Code workflow
Start with the free PDF, use Gumroad guides when you need repeatable workflows, and book consultation when rollout or revenue paths need human judgment.
About the Author
Masa
Engineer focused on practical Claude Code workflows. Runs claudecode-lab.com, a 10-language technical media site.
Related Posts
Claude Code Obsidian to CLAUDE.md Workflow: Stop Re-explaining Context
Turn Obsidian working notes into concise CLAUDE.md operating notes that make Claude Code sessions easier to resume.
Claude Code Revenue CTA Routing: Send Articles to PDF, Gumroad, and Consultation
A Claude Code workflow for routing article readers to the free PDF, Gumroad products, or consultation by intent.
Claude Code Team Handoff Rules: Review Evidence, Permissions, Rollback, and Revenue Paths
A practical Claude Code handoff format for team review, proof, permission rules, rollback, free PDF, Gumroad, and consultation paths.
Related Products
50 Battle-Tested Claude Code Prompt Templates
Copy, paste, ship. 50 production-ready prompts.
Use proven prompts for code review, refactoring, testing, documentation, debugging, architecture, and incident response.
The Complete Claude Code Setup & Configuration Guide
From install to team-ready workflow.
A practical guide to installation, CLAUDE.md, hooks, MCP servers, permissions, IDE setup, and CI/CD workflows.