Claude Code y OpenAPI 3.1: guía práctica con Swagger y TypeScript
Crea especificaciones OpenAPI 3.1 con Claude Code, valida con Swagger y genera clientes TypeScript.
Claude Code puede escribir un archivo OpenAPI en pocos minutos. El problema aparece cuando ese archivo se publica sin revisar: puede inventar campos a partir de una pantalla, añadir valores de enum que no existen en la base de datos o cambiar el formato de error entre endpoints.
En esta guía usaremos Claude Code como asistente para mantener contratos, no como autor infalible de esquemas. Vas a crear una especificación OpenAPI 3.1, validarla, generar un cliente TypeScript y un stub de servidor, y añadir una regla sencilla para detectar errores típicos de especificaciones generadas por IA. OpenAPI describe endpoints REST, cuerpos de petición, respuestas, autenticación y errores en un formato legible por herramientas. Swagger es el ecosistema que muchos equipos usan para editar, visualizar y revisar esos documentos.
Masa probó este flujo con una API pequeña de pedidos. El prompt inicial, “lee el repositorio y crea OpenAPI”, produjo un estado cancelled que sonaba razonable pero no existía en el dominio. El resultado mejoró cuando el prompt indicó qué archivos leer, prohibió campos no verificados, ejecutó validaciones y dejó las dudas fuera del esquema público.
Usa siempre fuentes oficiales. Al 3 de junio de 2026, la OpenAPI Specification latest ya es 3.2.0, pero esta guía fija los ejemplos en openapi: 3.1.0 por compatibilidad con generadores. Para los detalles de 3.1 usa OpenAPI Specification 3.1.2, para edición la documentación de Swagger Editor, y para generación OpenAPI Generator usage, typescript-fetch generator y typescript-nestjs-server generator. Para OpenAPI 3.1, revisa el soporte de Swagger Editor Next en la documentación actual antes de confiar en un editor antiguo.
Para completar el flujo, consulta también la guía de desarrollo de APIs, la guía de pruebas de API, las buenas prácticas de seguridad y las buenas prácticas de CLAUDE.md.
flowchart LR
A["Implementación, DB, tests"] --> B["Claude Code redacta OpenAPI"]
B --> C["Validación Swagger y CLI"]
C --> D["Generar cliente TypeScript"]
C --> E["Pruebas de contrato"]
C --> F["Revisión de seguridad"]
D --> G["Frontend o partners"]
E --> H["Gate de publicación CI"]
F --> H
Casos de uso reales
| Caso | Qué delegar a Claude Code | Qué decide el equipo |
|---|---|---|
| Documentar una API existente | Rutas, modelos, códigos HTTP y autenticación | Campos públicos, compatibilidad y nombres |
| Diseñar contrato antes del código | Borrador OpenAPI 3.1, ejemplos y comandos | Reglas de negocio y política de errores |
| Integrar frontend | Cliente TypeScript y ejemplos de llamada | UX, reintentos y mensajes al usuario |
| Pruebas de contrato | operationId, respuestas 4xx, enums y auth | Política de cambios incompatibles y excepciones |
| Revisión de seguridad | Schemas públicos, auth, PII e IDs internos | Riesgo legal, auditoría y compromisos con partners |
| Publicar API para partners | Descripciones, ejemplos y notas de auth | Contratos, límites de uso y soporte |
La regla práctica es simple: si el código, la base de datos, los tests o una decisión de producto no prueban un detalle del esquema, Claude Code no debe publicarlo.
Proyecto mínimo
Requiere Node.js 20 o superior. OpenAPI Generator usa Java para generar código, así que instala Java si quieres ejecutar los comandos de generación.
mkdir openapi-claude-demo
cd openapi-claude-demo
npm init -y
npm install -D @openapitools/openapi-generator-cli js-yaml
mkdir specs generated scripts
Configura 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"
}
}
Crea specs/openapi.yaml. En OpenAPI 3.1, para valores nulos usa JSON Schema, por ejemplo type: [string, "null"], en lugar de nullable: true.
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"
get:
operationId: listOrders
summary: List orders
tags: [orders]
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
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
Añade una revisión local para detectar marcadores temporales, patrones antiguos y campos sensibles:
// 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.");
Añade una prueba de contrato que pueda ejecutarse en CI. No sustituye a las pruebas contra el servidor real; verifica que el contrato público mantenga operationId, seguridad en operaciones mutantes, respuestas 4xx compartidas y el enum actual de 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"]);
});
Ejecuta:
npm run validate:openapi
npm run lint:contract
npm run test:contract
npm run generate:client
npm run generate:server
Los archivos generados no son la fuente de verdad. Corrige specs/openapi.yaml y vuelve a generar.
Prompt recomendado
Crea un contrato OpenAPI 3.1 para la API de pedidos.
Lee primero src/routes/orders.ts, src/domain/order.ts, prisma/schema.prisma y tests/orders.test.ts.
No inventes campos, enums ni códigos de error no demostrados por código, esquema, tests o nota de producto.
Coloca dudas en x-claude-review, no en schemas públicos.
No uses nullable: true. Usa operationId estable en camelCase.
Declara security en operaciones protegidas. Separa campos internos, PII y candidatos a secret en x-claude-review antes de publicarlos.
Ejecuta npm run validate:openapi, npm run lint:contract y npm run test:contract, y corrige fallos.
Devuelve el diff, un ejemplo de cliente TypeScript, notas de seguridad y dudas para revisión humana.
Pruebas de contrato y seguridad
En una API SaaS o de partners, revisar OpenAPI no es solo corregir texto. Separa cuatro carriles: revisión de especificación, revisión del cliente generado, pruebas de contrato y seguridad. La especificación cubre rutas, métodos, schemas, estados y ejemplos. El cliente generado valida que typescript-fetch produzca tipos útiles. Las pruebas de contrato frenan auth ausente, errores inconsistentes y enums rotos. Seguridad revisa campos públicos, PII, IDs internos, URLs de servidor y posibles secretos.
Claude Code puede inventariar diferencias, redactar pruebas y preparar la tabla de revisión. El equipo sigue siendo responsable de decidir qué se publica, qué rate limit se promete, qué datos tienen riesgo legal y qué evidencia necesita auditoría.
Fallos frecuentes
El primer fallo es inventar esquemas: cancelled, refunded o adminNote aparecen porque parecen útiles. El segundo es tener errores inconsistentes, como { message } en una ruta y { error: { code } } en otra. El tercero es mezclar hábitos de OpenAPI 3.0, especialmente nullable: true, dentro de un documento 3.1. El cuarto es editar a mano el cliente generado. El quinto es revisar solo la vista Swagger y olvidar operationId, autenticación, ejemplos y compatibilidad de enums.
CTA de formación
La limpieza de OpenAPI se vende bien como formación o consultoría porque deja entregables claros: especificación, cliente TypeScript, stub de servidor, prueba de contrato, validación y checklist de CI. Para trabajar de forma individual, usa los productos y plantillas de ClaudeCodeLab. Para un rollout con repositorio real, CLAUDE.md, pruebas de API y revisión de seguridad, usa formación y consultoría Claude Code.
Resultado probado
En la prueba de Masa, el prompt libre añadió estados no implementados y cambió el formato de error. Con archivos obligatorios, reglas de no invención, openapi-generator-cli validate, el script local y node --test, la revisión humana se redujo a cuatro decisiones: campos públicos, crecimiento futuro de enums, texto de errores de autenticación y posible fuga de datos internos. OpenAPI funcionó mejor como contrato compartido que como documento delegado a ciegas.
PDF gratis: cheatsheet de Claude Code
Introduce tu email y descarga una hoja con comandos, hábitos de revisión y flujos seguros.
Cuidamos tus datos y no enviamos spam.
Sobre el autor
Masa
Ingeniero enfocado en workflows prácticos con Claude Code.
Artículos relacionados
Workflow de Obsidian a CLAUDE.md con Claude Code
Convierte notas de trabajo de Obsidian en notas operativas de CLAUDE.md para no repetir contexto.
Claude Code Revenue CTA Routing: de artículos a PDF, Gumroad y consulta
Un flujo con Claude Code para dirigir lectores a PDF gratis, Gumroad o consulta según intención.
Reglas de handoff para equipos con Claude Code: evidencia, permisos, rollback e ingresos
Formato práctico para entregar trabajo de Claude Code con pruebas, permisos, rollback, PDF gratis, Gumroad y consulta.