Claude Code et OpenAPI 3.1 : validation Swagger et génération TypeScript
Créez des specs OpenAPI 3.1 avec Claude Code, validez-les et générez un client TypeScript fiable.
Claude Code peut produire une spécification OpenAPI très vite. Ce n’est pas suffisant pour la publier. Sans garde-fous, il peut ajouter un champ vu dans une maquette, inventer une valeur d’enum ou modifier la forme des erreurs d’une route à l’autre.
Ce guide montre comment l’utiliser comme assistant de maintenance de contrat API. Nous allons créer une spécification OpenAPI 3.1, la valider, générer un client TypeScript et un stub serveur, puis ajouter un petit contrôle local contre les schémas hallucinés. OpenAPI décrit les endpoints REST, les requêtes, les réponses, l’authentification et les erreurs. Swagger désigne l’écosystème d’outils utilisé pour éditer, afficher et vérifier ces documents.
Masa a testé ce flux sur une petite API de commandes. Le prompt libre a généré un statut cancelled inexistant dans le modèle métier. Le flux fiable consistait à imposer les fichiers à lire, interdire les champs non prouvés, valider la spec et sortir les questions ouvertes du schéma public.
Références officielles : au 3 juin 2026, OpenAPI Specification latest pointe vers 3.2.0, mais les exemples de cet article restent en openapi: 3.1.0 pour la compatibilité des générateurs. Utilisez OpenAPI Specification 3.1.2, Swagger Editor documentation, OpenAPI Generator usage, typescript-fetch generator et typescript-nestjs-server generator. Pour OpenAPI 3.1, vérifiez le support de Swagger Editor Next dans la documentation actuelle.
À lire aussi : développement API avec Claude Code, tests API, sécurité Claude Code et bonnes pratiques CLAUDE.md.
flowchart LR
A["Code, base, tests"] --> B["Claude Code rédige OpenAPI"]
B --> C["Validation Swagger et CLI"]
C --> D["Client TypeScript"]
C --> E["Tests de contrat"]
C --> F["Revue sécurité"]
D --> G["Frontend ou partenaires"]
E --> H["Gate CI"]
F --> H
Trois usages concrets
| Usage | Travail de Claude Code | Décision humaine |
|---|---|---|
| Documenter une API existante | Routes, modèles, codes HTTP, auth | Champs publics, compatibilité, nommage |
| Démarrer en contract-first | Brouillon OpenAPI 3.1, exemples, commandes | Règles métier, erreurs, périmètre |
| Brancher le frontend | Client TypeScript, exemples d’appel | UX, retry, messages utilisateurs |
| Tester le contrat | operationId, 4xx, enums, auth | Politique de breaking change et exceptions |
| Revue sécurité | Schemas publics, auth, PII, IDs internes | Risque légal, audit, promesses aux partenaires |
| Publier à des partenaires | Descriptions Swagger, exemples, auth | Contrats, quotas, support |
Si un détail de schéma n’est pas confirmé par le code, la base, les tests ou une décision produit, il doit rester en question ouverte.
Mise en place copiable
Node.js 20+ est recommandé. OpenAPI Generator utilise Java pour la génération.
mkdir openapi-claude-demo
cd openapi-claude-demo
npm init -y
npm install -D @openapitools/openapi-generator-cli js-yaml
mkdir specs generated scripts
Ajoutez ces 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"
}
}
Créez 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
Ajoutez 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.");
Ajoutez aussi un test de contrat exécutable en CI. Il vérifie le contrat public : operationId, sécurité sur les opérations mutantes, réponses 4xx partagées et enum 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"]);
});
Puis lancez :
npm run validate:openapi
npm run lint:contract
npm run test:contract
npm run generate:client
npm run generate:server
Prompt à donner à Claude Code
Crée un contrat OpenAPI 3.1 pour l'API de commandes.
Lis src/routes/orders.ts, src/domain/order.ts, prisma/schema.prisma et tests/orders.test.ts.
N'invente aucun champ, enum ou code d'erreur non prouvé.
Place les questions dans x-claude-review, pas dans les schemas publics.
N'utilise pas nullable: true. Utilise des operationId camelCase stables.
Déclare security sur les opérations protégées. Sépare les champs internes, PII et secrets possibles dans x-claude-review.
Exécute npm run validate:openapi, npm run lint:contract et npm run test:contract, puis corrige les erreurs.
Retourne le diff, un exemple de client TypeScript, les notes sécurité et les points à valider humainement.
Tests de contrat et sécurité
Pour une API SaaS ou partenaire, la revue OpenAPI doit être découpée. La revue de spec regarde routes, méthodes, schemas, statuts et exemples. La revue du client généré vérifie les types typescript-fetch et le traitement de null. Les tests de contrat bloquent les auth manquantes, les erreurs incohérentes et les enums cassés. La revue sécurité vérifie les champs publics, PII, IDs internes, URLs serveur et secrets possibles.
Claude Code peut préparer les diffs, les tests et le tableau de revue. L’équipe garde la décision sur les champs publiables, les limites de débit, les engagements contractuels et les preuves d’audit.
Pièges fréquents
Les problèmes récurrents sont les schémas inventés, les formats d’erreur incohérents, les habitudes OpenAPI 3.0 dans un fichier 3.1, la modification manuelle du code généré et la revue uniquement visuelle dans Swagger. Le fichier specs/openapi.yaml reste la source de vérité ; les clients et stubs se régénèrent.
Formation et conseil
Cette démarche se vend bien en formation parce que les livrables sont visibles : spec OpenAPI, client TypeScript, stub serveur, test de contrat, validation et checklist CI. Pour travailler seul, consultez les produits ClaudeCodeLab. Pour une équipe avec dépôt réel, CLAUDE.md, tests API et revue sécurité, utilisez la page formation et conseil Claude Code.
Vérification terrain
Lors du test de Masa, le prompt libre a ajouté des statuts inexistants et modifié les erreurs. Avec les fichiers imposés, l’interdiction d’inventer, openapi-generator-cli validate, le script local et node --test, la revue humaine s’est limitée aux champs publics, à l’évolution des enums, au texte des erreurs d’authentification et au risque de fuite d’informations internes. OpenAPI a été le socle commun entre l’IA et l’équipe, pas un document abandonné à l’IA.
PDF gratuit: cheatsheet Claude Code
Saisissez votre email et téléchargez une page avec commandes, habitudes de review et workflow sûr.
Nous protégeons vos données et n'envoyons pas de spam.
À propos de l'auteur
Masa
Ingénieur spécialisé dans les workflows pratiques avec Claude Code.
Articles liés
Workflow Obsidian vers CLAUDE.md avec Claude Code
Transformer des notes Obsidian en notes CLAUDE.md concises pour reprendre les sessions sans réexpliquer.
Claude Code Revenue CTA Routing : relier articles, PDF, Gumroad et consultation
Un workflow Claude Code pour orienter les lecteurs vers PDF gratuit, Gumroad ou consultation selon l'intention.
Règles de handoff Claude Code en équipe: preuves, permissions, rollback et revenus
Un format concret pour transmettre un travail Claude Code avec preuves, permissions, rollback, PDF gratuit, Gumroad et consultation.