8 min lectura • Guide 348 of 877
Mejores Prácticas de Diseño de APIs
Un buen diseño de API hace la vida de los consumidores más fácil y reduce la carga de soporte. Un mal diseño lleva a confusión, bugs y desarrolladores frustrados. Esta guía cubre enfoques prácticos de diseño de API que resisten la prueba del tiempo.
Principios de API
PRINCIPIOS FUNDAMENTALES:
┌─────────────────────────────────────────────────────────────┐
│ │
│ PRINCIPIO │ DESCRIPCIÓN │ BENEFICIO │
│ ───────────────┼───────────────────────┼───────────────────│
│ Consistencia │ Mismos patrones │ Facilidad de │
│ │ en todo │ aprendizaje │
│ ───────────────┼───────────────────────┼───────────────────│
│ Predecibilidad │ Comportamiento │ Menos bugs │
│ │ esperado │ │
│ ───────────────┼───────────────────────┼───────────────────│
│ Evolucionabilidad│ Cambiar de forma │ Longevidad │
│ │ segura │ │
│ ───────────────┼───────────────────────┼───────────────────│
│ Simplicidad │ Fácil de usar │ Adopción │
└─────────────────────────────────────────────────────────────┘
Diseño RESTful
API Orientada a Recursos
DISEÑO DE API RESTFUL:
┌─────────────────────────────────────────────────────────────┐
│ │
│ RECURSOS (Piensa en sustantivos, no verbos): │
│ ───────────────────────────────────────────── │
│ ├── /users (NO /getUsers) │
│ ├── /projects (NO /createProject) │
│ ├── /tasks (NO /deleteTask) │
│ ├── Recursos son COSAS │
│ └── Métodos HTTP son ACCIONES │
│ │
│ OPERACIONES CRUD: │
│ ───────────────── │
│ Método HTTP │ Operación │ Ejemplo │
│ ────────────┼───────────┼──────────────────────────────────│
│ GET │ Leer │ GET /users │
│ POST │ Crear │ POST /users │
│ PUT │ Reemplazar│ PUT /users/123 │
│ PATCH │ Actualizar│ PATCH /users/123 │
│ DELETE │ Eliminar │ DELETE /users/123 │
│ │
│ JERARQUÍA DE RECURSOS: │
│ ────────────────────── │
│ Recursos anidados: │
│ ├── GET /projects/123/tasks │
│ ├── POST /projects/123/tasks │
│ ├── GET /projects/123/tasks/456 │
│ ├── Relaciones claras │
│ └── Jerarquía lógica │
│ │
│ ESTRUCTURA DE URL: │
│ ────────────────── │
│ Nombrado consistente: │
│ ├── Sustantivos en plural: /users, /tasks │
│ ├── Minúsculas: /user-profiles │
│ ├── Guiones para multi-palabra │
│ ├── Sin trailing slashes │
│ ├── Sin extensiones de archivo │
│ └── Limpio, predecible │
└─────────────────────────────────────────────────────────────┘
Request/Response
Formato de Datos
DISEÑO DE REQUEST/RESPONSE:
┌─────────────────────────────────────────────────────────────┐
│ │
│ JSON CONSISTENTE: │
│ ───────────────── │
│ Siempre la misma estructura: │
│ │
│ Respuesta de éxito: │
│ { │
│ "data": { │
│ "id": 123, │
│ "name": "Juan Pérez", │
│ "email": "juan@ejemplo.com" │
│ } │
│ } │
│ │
│ Respuesta de lista: │
│ { │
│ "data": [ │
│ { "id": 123, "name": "Juan" }, │
│ { "id": 124, "name": "María" } │
│ ], │
│ "meta": { │
│ "total": 100, │
│ "page": 1, │
│ "perPage": 20 │
│ } │
│ } │
│ │
│ CONVENCIONES DE NOMBRADO: │
│ ────────────────────────── │
│ Casing consistente: │
│ ├── camelCase para JSON: firstName │
│ ├── snake_case también común: first_name │
│ ├── Elige uno, usa en todas partes │
│ ├── Coincide con convenciones de tu lenguaje │
│ └── La consistencia es lo que más importa │
│ │
│ FILTRADO DE CAMPOS: │
│ ─────────────────── │
│ Permite a clientes solicitar campos: │
│ ├── GET /users?fields=id,name,email │
│ ├── Reduce sobre-fetching │
│ ├── Mejor performance │
│ ├── Enfoque GraphQL para REST │
│ └── Opcional pero útil │
└─────────────────────────────────────────────────────────────┘
Códigos de Estado HTTP
Uso Correcto
CÓDIGOS DE ESTADO HTTP:
┌─────────────────────────────────────────────────────────────┐
│ │
│ ÉXITO (2xx): │
│ ───────────── │
│ 200 OK → GET/PUT/PATCH exitoso │
│ 201 Created → POST exitoso (recurso creado) │
│ 204 No Content → DELETE exitoso (sin body) │
│ │
│ ERRORES DEL CLIENTE (4xx): │
│ ─────────────────────────── │
│ 400 Bad Request → Input inválido (formato, sintaxis) │
│ 401 Unauthorized→ Sin autenticación │
│ 403 Forbidden → Sin permiso (autenticado pero sin acceso)│
│ 404 Not Found → Recurso no existe │
│ 409 Conflict → Duplicado/conflicto │
│ 422 Unprocessable→ Validación falló (semánticamente mal) │
│ 429 Too Many → Rate limit excedido │
│ │
│ ERRORES DEL SERVIDOR (5xx): │
│ ──────────────────────────── │
│ 500 Server Error→ Error nuestro (culpa del servidor) │
│ 502 Bad Gateway → Servicio upstream falló │
│ 503 Unavailable → Servicio temporalmente no disponible │
│ │
│ ERRORES COMUNES: │
│ ──────────────── │
│ ❌ 200 para errores (con mensaje de error en body) │
│ ❌ 500 para errores de validación │
│ ❌ 404 cuando debería ser 403 │
│ ✅ Usar código específico para cada situación │
└─────────────────────────────────────────────────────────────┘
Manejo de Errores
Respuestas de Error
FORMATO DE RESPUESTA DE ERROR:
┌─────────────────────────────────────────────────────────────┐
│ │
│ ESTRUCTURA RECOMENDADA: │
│ ──────────────────────── │
│ { │
│ "error": { │
│ "code": "VALIDATION_ERROR", │
│ "message": "Los datos proporcionados son inválidos", │
│ "details": [ │
│ { │
│ "field": "email", │
│ "message": "Formato de email inválido" │
│ }, │
│ { │
│ "field": "name", │
│ "message": "El nombre es requerido" │
│ } │
│ ], │
│ "requestId": "req_abc123" │
│ } │
│ } │
│ │
│ COMPONENTES: │
│ ───────────── │
│ code → Identificador único del tipo de error │
│ message → Mensaje legible para humanos │
│ details → Errores específicos por campo (si aplica) │
│ requestId → Para debugging/soporte │
│ │
│ MENSAJES DE ERROR: │
│ ────────────────── │
│ ❌ "Error" │
│ ❌ "Something went wrong" │
│ ✅ "El email 'abc' no tiene formato válido" │
│ ✅ "No tienes permiso para eliminar este proyecto" │
│ │
│ Mensajes claros, específicos y accionables │
└─────────────────────────────────────────────────────────────┘
Versionado
Estrategias de Versión
ESTRATEGIAS DE VERSIONADO DE API:
┌─────────────────────────────────────────────────────────────┐
│ │
│ OPCIÓN 1: VERSIONADO EN URL (Recomendado) │
│ ────────────────────────────────────────── │
│ /api/v1/users │
│ /api/v2/users │
│ │
│ ✅ Más explícito y visible │
│ ✅ Fácil de cachear │
│ ✅ Claro en logs y debugging │
│ ❌ Múltiples URLs para mismo recurso │
│ │
│ OPCIÓN 2: VERSIONADO EN HEADER │
│ ────────────────────────────── │
│ Accept: application/vnd.api+json;version=1 │
│ │
│ ✅ URL limpia │
│ ❌ Menos visible │
│ ❌ Más difícil de probar │
│ │
│ OPCIÓN 3: QUERY PARAMETER │
│ ────────────────────────── │
│ /api/users?version=1 │
│ │
│ ✅ Simple │
│ ❌ Puede confundirse con otros params │
│ │
│ CUÁNDO INCREMENTAR VERSIÓN: │
│ ──────────────────────────── │
│ v1 → v2 cuando: │
│ • Cambio en estructura de response │
│ • Campos removidos o renombrados │
│ • Cambio en comportamiento de endpoint │
│ • Cualquier cambio que rompa clientes │
│ │
│ NO necesita nueva versión: │
│ • Agregar campos opcionales │
│ • Nuevo endpoint │
│ • Corrección de bugs │
└─────────────────────────────────────────────────────────────┘
Paginación
Patrones de Paginación
PAGINACIÓN DE API:
┌─────────────────────────────────────────────────────────────┐
│ │
│ OFFSET-BASED (Más común): │
│ ────────────────────────── │
│ GET /users?page=2&perPage=20 │
│ │
│ Response: │
│ { │
│ "data": [...], │
│ "meta": { │
│ "total": 150, │
│ "page": 2, │
│ "perPage": 20, │
│ "totalPages": 8 │
│ } │
│ } │
│ │
│ ✅ Simple de implementar │
│ ❌ Puede perder/duplicar ítems si datos cambian │
│ │
│ CURSOR-BASED (Más robusto): │
│ ─────────────────────────── │
│ GET /users?cursor=eyJpZCI6MTIzfQ==&limit=20 │
│ │
│ Response: │
│ { │
│ "data": [...], │
│ "cursors": { │
│ "next": "eyJpZCI6MTQzfQ==", │
│ "prev": "eyJpZCI6MTAzfQ==" │
│ } │
│ } │
│ │
│ ✅ Consistente incluso si datos cambian │
│ ✅ Mejor para feeds/timelines │
│ ❌ No puede saltar a página específica │
└─────────────────────────────────────────────────────────────┘