Diseño de REST API con Claude Code: endpoints, errores, paginación e idempotencia
Diseña REST APIs con Claude Code antes de programar: OpenAPI, errores, paginación, idempotencia y checklist.
Una REST API no está terminada solo porque existan unas cuantas rutas. Antes de implementar, conviene decidir qué será un recurso, qué método HTTP usará cada operación, qué devolverá la API cuando falle, cómo paginará las listas y qué ocurrirá si recibe la misma petición dos veces. Si esas decisiones quedan abiertas, Claude Code puede escribir código rápido, pero el frontend, la app móvil y las integraciones externas acabarán interpretando contratos distintos.
El objetivo de este artículo es usar Claude Code para cerrar el diseño de la REST API antes de tocar la implementación. REST significa Representational State Transfer, pero para trabajar en producto basta con verlo como un conjunto de reglas para operar recursos como pedidos, usuarios o facturas a través de HTTP. El recurso es el objeto, el método es la operación y el código de estado es la señal del resultado.
Como referencias oficiales, usa HTTP request methods y HTTP response status codes de MDN, además de la OpenAPI Specification. A fecha 3 de junio de 2026, la página latest de OpenAPI apunta a 3.2.0; el ejemplo usa openapi: 3.1.0 porque muchas herramientas de lint y generación todavía lo soportan de forma más predecible.
Para profundizar en implementación, revisa desarrollo de APIs con Claude Code, testing de APIs con Claude Code y code review con Claude Code. Si quieres plantillas y checklists reutilizables, están en /products/. Para incorporar revisiones de diseño de API en tu equipo, consulta /training/.
Alinear El Vocabulario Primero
Quien empieza suele confundirse porque los términos de REST se parecen. Antes de pedir implementación a Claude Code, conviene que el equipo use estas palabras con el mismo significado.
| Término | Explicación sencilla | Ejemplo |
|---|---|---|
| Recurso | El sustantivo que maneja la API | orders, customers |
| Endpoint | La combinación de URL y método HTTP | GET /v1/orders |
| Método | El verbo HTTP que indica la operación | GET, POST, PATCH |
| Código de estado | Número que comunica éxito, error de entrada, falta de autenticación, etc. | 200, 201, 400, 404 |
| Validación | Comprobación de que la petición tiene la forma correcta | Formato de email, cantidad mínima |
| Idempotencia | Propiedad por la que repetir la misma operación no cambia el estado final | Crear un solo pedido para la misma clave |
Claude Code entiende contexto, pero no puede garantizar que adivine tu intención de diseño. “Crea la API para pedidos” puede convertirse en /createOrder, /orders/create o POST /orders. Pasar vocabulario y reglas antes del código mejora mucho el resultado.
Decidir Con Tres Casos De Uso
La misma API de pedidos cambia según cómo se consuma.
El primer caso es una integración B2B SaaS. Si un partner reintenta el mismo CSV nocturno, POST /v1/orders necesita Idempotency-Key para no crear pedidos duplicados. Los reintentos aparecen durante incidentes de red, así que la idempotencia es difícil de añadir bien al final.
El segundo caso es una lista de administración. Una persona filtra por status=paid, avanza a la siguiente página y otra persona crea un pedido mientras tanto. page=2 puede desplazarse. Es mejor usar paginación por cursor con limit y after, y fijar el orden, por ejemplo createdAt desc, id desc.
El tercer caso es una API para aplicación móvil. Si las versiones antiguas permanecen instaladas durante meses, no puedes cambiar nombres de campos de respuesta de golpe. Nuevos campos obligatorios o un nuevo formato de error deben ir a /v2, mientras el contrato de /v1 sigue en OpenAPI. Añadir campos opcionales normalmente no exige nueva versión.
Un cuarto caso habitual son APIs internas entre servicios. Es más fácil actualizar consumidores a la vez, pero el monitoreo suele ser más débil. Incluso dentro de la empresa, códigos de estado y errores inconsistentes obligan a cada consumidor a escribir excepciones propias.
Ver El Flujo De Diseño
Fija el diseño en este orden antes de entregar la tarea a Claude Code. Así la implementación se mantiene cerca del contrato mientras se editan archivos.
flowchart TD
A["Use cases"] --> B["Resources and endpoints"]
B --> C["OpenAPI contract"]
C --> D["Errors, pagination, idempotency"]
D --> E["Claude Code implementation"]
E --> F["Tests and review"]
Fijar La Tabla De Endpoints Antes De Programar
El primer entregable no es código, sino una tabla de endpoints. Entrégala a Claude Code para que rutas, OpenAPI, pruebas y documentación salgan del mismo contrato.
| Propósito | Método y ruta | Éxito | Regla importante |
|---|---|---|---|
| Listar pedidos | GET /v1/orders?status=paid&limit=20&after=ord_123 | 200 OK | Paginación por cursor con orden estable |
| Crear pedido | POST /v1/orders | 201 Created | Aceptar Idempotency-Key y devolver Location |
| Ver pedido | GET /v1/orders/{orderId} | 200 OK | Si no existe, devolver 404 |
| Actualizar pedido | PATCH /v1/orders/{orderId} | 200 OK | Actualización parcial; body vacío es 400 |
| Cancelar pedido | POST /v1/orders/{orderId}/cancel | 200 OK | Tratar transiciones de estado de forma consistente |
La regla general es “sustantivos en la URL, verbos en el método”. Algunas acciones de negocio, como cancelar un pedido, se entienden mejor como subacción explícita que forzándolas a DELETE. Lo importante es documentar la excepción y mantener el patrón en operaciones parecidas.
Usar OpenAPI Como Fuente Del Diseño
OpenAPI describe rutas, parámetros, peticiones y respuestas en un formato que las herramientas pueden leer. Si indicas a Claude Code que este contrato OpenAPI es la fuente de verdad, reduces la libertad de implementación al espacio correcto.
openapi: 3.1.0
info:
title: Acme Orders API
version: 1.0.0
servers:
- url: https://api.example.com
paths:
/v1/orders:
get:
operationId: listOrders
summary: List orders
parameters:
- $ref: "#/components/parameters/StatusParam"
- $ref: "#/components/parameters/LimitParam"
- $ref: "#/components/parameters/AfterParam"
responses:
"200":
description: Orders are returned in a stable cursor order.
content:
application/json:
schema:
$ref: "#/components/schemas/OrderListResponse"
"400":
$ref: "#/components/responses/BadRequest"
post:
operationId: createOrder
summary: Create an order
parameters:
- $ref: "#/components/parameters/IdempotencyKey"
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateOrderRequest"
responses:
"201":
description: Order created.
headers:
Location:
schema:
type: string
description: URL of the created order.
content:
application/json:
schema:
$ref: "#/components/schemas/OrderResponse"
"400":
$ref: "#/components/responses/BadRequest"
"409":
$ref: "#/components/responses/Conflict"
/v1/orders/{orderId}:
get:
operationId: getOrder
summary: Get one order
parameters:
- $ref: "#/components/parameters/OrderId"
responses:
"200":
description: Order found.
content:
application/json:
schema:
$ref: "#/components/schemas/OrderResponse"
"404":
$ref: "#/components/responses/NotFound"
patch:
operationId: updateOrder
summary: Update mutable order fields
parameters:
- $ref: "#/components/parameters/OrderId"
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/UpdateOrderRequest"
responses:
"200":
description: Order updated.
content:
application/json:
schema:
$ref: "#/components/schemas/OrderResponse"
"400":
$ref: "#/components/responses/BadRequest"
"404":
$ref: "#/components/responses/NotFound"
components:
parameters:
OrderId:
name: orderId
in: path
required: true
schema:
type: string
StatusParam:
name: status
in: query
schema:
type: string
enum: [draft, paid, canceled]
LimitParam:
name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 100
default: 20
AfterParam:
name: after
in: query
schema:
type: string
description: Cursor returned by the previous response.
IdempotencyKey:
name: Idempotency-Key
in: header
required: false
schema:
type: string
minLength: 16
maxLength: 128
schemas:
CreateOrderRequest:
type: object
required: [customerId, items]
properties:
customerId:
type: string
items:
type: array
minItems: 1
items:
type: object
required: [sku, quantity]
properties:
sku:
type: string
quantity:
type: integer
minimum: 1
UpdateOrderRequest:
type: object
minProperties: 1
properties:
status:
type: string
enum: [draft, paid, canceled]
OrderResponse:
type: object
required: [data]
properties:
data:
$ref: "#/components/schemas/Order"
OrderListResponse:
type: object
required: [data, pageInfo]
properties:
data:
type: array
items:
$ref: "#/components/schemas/Order"
pageInfo:
type: object
required: [nextCursor, hasMore]
properties:
nextCursor:
type: [string, "null"]
hasMore:
type: boolean
Order:
type: object
required: [id, status, totalCents, createdAt]
properties:
id:
type: string
status:
type: string
totalCents:
type: integer
createdAt:
type: string
format: date-time
ErrorResponse:
type: object
required: [error]
properties:
error:
type: object
required: [code, message, requestId]
properties:
code:
type: string
message:
type: string
requestId:
type: string
details:
type: array
items:
type: object
responses:
BadRequest:
description: Request validation failed.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
Conflict:
description: Idempotency key conflicts with a different request.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
NotFound:
description: Resource was not found.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
Guárdalo como openapi.yaml y pide a Claude Code que no proponga implementaciones que se aparten del contrato. Después, una herramienta como npx @redocly/cli lint openapi.yaml puede validar sintaxis y referencias. Si generas tipos o clientes TypeScript desde OpenAPI, revisa antes el significado humano de la tabla.
Unificar Errores Y Códigos De Estado
El código de estado es la señal HTTP externa. El JSON de error contiene el detalle que leen personas y programas. Devolver 200 OK con { "success": false } hace que monitoreo, SDKs y reintentos traten el fallo como éxito. Fija una tabla: entrada inválida es 400, sin autenticación es 401, sin permisos es 403, recurso inexistente es 404, duplicado o conflicto de idempotencia es 409.
{
"error": {
"code": "VALIDATION_FAILED",
"message": "Request body has invalid fields.",
"requestId": "req_01J0RESTAPI001",
"details": [
{
"field": "items[0].quantity",
"reason": "must be greater than or equal to 1"
}
]
}
}
{
"error": {
"code": "IDEMPOTENCY_CONFLICT",
"message": "The same Idempotency-Key was used with a different request body.",
"requestId": "req_01J0RESTAPI002"
}
}
{
"error": {
"code": "ORDER_NOT_FOUND",
"message": "Order was not found.",
"requestId": "req_01J0RESTAPI003"
}
}
Para principiantes: code es el nombre corto para que el programa decida una rama, message es la explicación para logs o interfaz, y requestId permite encontrar los logs del servidor. details solo hace falta cuando hay errores de validación por campo.
No Dejar Ambiguas La Paginación, La Idempotencia Ni La Versión
Las APIs de lista necesitan paginación desde el inicio. Devolver todos los pedidos funciona con pocos datos, pero cuando la tabla crece cambiar la respuesta se convierte en breaking change. Con paginación por cursor, el cliente envía el nextCursor anterior como nuevo after.
{
"data": [
{
"id": "ord_100",
"status": "paid",
"totalCents": 4800,
"createdAt": "2026-06-03T09:00:00Z"
}
],
"pageInfo": {
"nextCursor": "ord_099",
"hasMore": true
}
}
La idempotencia evita que peticiones repetidas creen efectos duplicados. POST /orders es especialmente delicado porque los clientes reintentan tras fallos de red. Guarda Idempotency-Key y un hash del body durante un tiempo limitado. Si llega la misma clave con el mismo body, devuelve el resultado anterior; si llega la misma clave con otro body, devuelve 409.
El versionado da tiempo de migración a los consumidores. Quitar un campo obligatorio, cambiar un tipo, modificar el orden por defecto o añadir un campo obligatorio al request es breaking change y debe ir a /v2. Añadir un campo opcional normalmente puede quedarse en /v1.
Pedir A Claude Code Una Revisión De Diseño
Antes de implementar, usa un prompt concreto.
Review this OpenAPI design.
Check:
- Resource names are nouns
- HTTP methods and status codes match MDN semantics
- 400/401/403/404/409/500 errors share the same JSON shape
- List pagination is unambiguous
- POST creation endpoints that need idempotency keys are identified
- No response change breaks the v1 contract
- Return a table of issues to fix before TypeScript implementation and tests
Así Claude Code actúa primero como revisor de diseño, antes de editar archivos. Pide la implementación solo después de guardar el OpenAPI revisado.
Errores Frecuentes
El primer error es crear endpoints con estilo de acción. Si conviven POST /createOrder, POST /updateOrderStatus y GET /deleteOrder, la URL ya no comunica recurso y operación. Prefiere POST /v1/orders, PATCH /v1/orders/{orderId} y DELETE /v1/orders/{orderId}, y documenta excepciones.
El segundo error es mezclar códigos de estado. Si una entrada inválida devuelve 500, un recurso inexistente devuelve 200 y una creación exitosa alterna entre 200 y 201, los clientes no pueden tomar decisiones fiables. Estandariza también los códigos de éxito.
El tercer error es paginación ambigua. Si page=2 no define si el orden es por creación o actualización, los datos nuevos causan duplicados o saltos. Documenta orden, limit máximo y cómo detectar la siguiente página.
El cuarto error es esconder validación solo en la implementación. Si quantity >= 1 está en el código pero no en OpenAPI, el frontend y los SDK generados no pueden reutilizar la regla. Las restricciones deben vivir en la especificación y en el código.
Checklist De Revisión
- Los recursos usan sustantivos plurales de forma consistente
- Los métodos respetan el significado de
GET,POST,PATCHyDELETE - Los éxitos distinguen
200,201y204 400,401,403,404,409y500comparten una forma JSON- Las listas incluyen
limit,after,nextCursoryhasMore limittiene valor por defecto y máximo documentados- Los endpoints de creación no duplican datos al reintentar
- Los criterios para mover breaking changes a
/v2están escritos - Las reglas de validación aparecen en OpenAPI
- La tabla de endpoints y OpenAPI coinciden antes de pasar la tarea a Claude Code
Diseñar una REST API no consiste en hacer URLs bonitas. Consiste en convertir promesas para los consumidores en texto legible y especificaciones que las herramientas puedan leer. Claude Code acelera la implementación, pero las promesas que el sistema debe cumplir las decide el equipo.
Al probarlo en la API de pedidos de Masa, preparar primero OpenAPI, la tabla de endpoints y los JSON de error redujo de forma clara el retrabajo en revisión. Incluir Idempotency-Key y paginación por cursor desde el diseño evitó parches posteriores para pedidos duplicados y registros omitidos en las listas.
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
Escalera de permisos de Claude Code para ampliar acceso sin perder control
Pasa de read-only a ediciones limitadas, comandos de prueba y checks de deploy con menos riesgo.
Claude Code Small PR Proof Pack: cambios pequeños que sí se pueden revisar
Un paquete de prueba para PRs de Claude Code: diff, checks, URL pública, CTA y rollback.
Gate de revisión antes del commit con Claude Code
Cómo revisar con Claude Code antes del commit: diff, build, URL pública, Gumroad, consultoría, tests y archivos ajenos.