# API Design — Fundamental Skill

Skill para diseñar APIs REST predecibles y consistentes. Usar al crear endpoints, definir respuestas, o manejar errores.

---

## Convenciones REST

### Verbos HTTP
- GET — Leer recurso(s). Idempotente. → 200
- POST — Crear recurso. No idempotente. → 201 + Location header
- PUT — Reemplazar completo. Idempotente. → 200
- PATCH — Actualizar parcial. Idempotente. → 200
- DELETE — Eliminar. Idempotente. → 204

### Reglas de URLs
- Recursos en plural, inglés: ✅ /api/v1/users ❌ /api/v1/user
- Jerarquía máximo 2 niveles: ✅ /users/42/orders ❌ /users/42/orders/17/items/3
- kebab-case: ✅ /payment-methods ❌ /paymentMethods
- Sin verbos en URL: ✅ DELETE /users/42 ❌ POST /users/42/delete
- Sub-recursos para relaciones: ✅ /users/42/orders ❌ /orders?userId=42
- Query params para filtros: ✅ /orders?status=paid&sort=-createdAt

### Acciones no-CRUD
- ✅ POST /orders/83/cancel (sub-recurso)
- ✅ POST /users/search { "query": {...} } (búsqueda compleja)

---

## Formato de respuesta estándar

### Recurso único
```json
{
  "data": { ... },
  "meta": {
    "requestId": "req_abc123",
    "timestamp": "2026-05-14T10:30:00Z"
  }
}
```

### Lista
```json
{
  "data": [ ... ],
  "meta": {
    "page": 1,
    "perPage": 20,
    "total": 142,
    "totalPages": 8,
    "requestId": "req_abc123"
  }
}
```

### Error
```json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "El campo email es requerido.",
    "details": [
      { "field": "email", "reason": "required" }
    ]
  },
  "meta": {
    "requestId": "req_abc123",
    "timestamp": "2026-05-14T10:30:00Z"
  }
}
```

### Reglas del envelope
1. Respuesta exitosa bajo `data`, errores bajo `error`
2. `requestId` en `meta` para tracear requests
3. Listas con `meta` de paginación
4. Errores de validación con array `details`
5. Nunca exponer stack traces en producción
6. Timestamps en ISO 8601 UTC

---

## Status Codes

- 200 OK — GET, PUT, PATCH exitosos
- 201 Created — POST exitoso. Incluir Location header
- 204 No Content — DELETE exitoso
- 400 Bad Request — Request malformado, JSON inválido
- 401 Unauthorized — Token ausente/expirado
- 403 Forbidden — Sin permisos
- 404 Not Found — Recurso no existe
- 409 Conflict — Violación de unicidad
- 422 Unprocessable — Validación de negocio
- 429 Too Many — Rate limit. Incluir Retry-After
- 500 Internal — Error inesperado (nunca a propósito)
- 503 Unavailable — Mantenimiento/dependencia caída

### Códigos de error en body
- VALIDATION_ERROR (400/422): Datos inválidos, con details[]
- AUTHENTICATION_ERROR (401): Token inválido
- AUTHORIZATION_ERROR (403): Sin permisos
- NOT_FOUND (404): Recurso no existe
- CONFLICT (409): Recurso ya existe
- RATE_LIMIT_ERROR (429): Excediste límite
- INTERNAL_ERROR (500): Error del servidor

### Reglas para errores
1. Nunca 200 con `{ "error": "..." }`. El status code debe reflejar el error.
2. `error.message` para usuario final. Stack solo en desarrollo con flag.
3. 5xx: loguear con requestId, timestamp, stack trace.
4. Validaciones: responder con TODOS los campos que fallaron.

---

## Versionado, Paginación y Filtros

### Versionado
- Estrategia: URL prefix `/api/v1/`. Visible, cache-friendly.
- ❌ Header (Accept: application/vnd.api.v1+json) — invisible, rompe CDN
- ❌ Query param (?version=1) — fácil de olvidar
- Breaking change → nueva versión. Campo nuevo → misma versión.
- Deprecar v1 con warning header por 6 meses.

### Paginación
Query params: `?page=1&perPage=20` (máximo 100)
Meta: `{ page, perPage, total, totalPages }`
Usar cursor-based para datasets que cambian frecuentemente.

### Filtros
- `?sort=-createdAt` (prefijo - para descendente)
- `?status=active` (igualdad exacta)
- `?q=fundamental` (búsqueda textual)
- `?from=2026-01-01&to=2026-01-31` (rango de fechas ISO 8601)
