Diseñar una API REST desde Cero

Diseño De Apis Intermediate 25 min Jan 12, 2026

Audiencia

Esta guía es para desarrolladores que quieren diseñar APIs REST intuitivas, consistentes y mantenibles:

  • Desarrolladores backend que construyen APIs desde cero y quieren seguir buenas prácticas
  • Líderes técnicos que establecen estándares de diseño de APIs para sus equipos
  • Desarrolladores frontend que quieren entender qué hace que una API esté bien diseñada
  • Cualquiera que haya luchado con APIs inconsistentes y quiera hacerlo mejor

Deberías entender métodos HTTP y códigos de estado. Si no, empieza con HTTP para APIs REST.

Objetivo

Después de leer esta guía, serás capaz de:

  • Diseñar URLs que sigan convenciones REST y sean intuitivas de usar
  • Modelar recursos y sus relaciones correctamente
  • Implementar paginación, filtrado y ordenación que escale
  • Crear respuestas de error consistentes que ayuden a los desarrolladores a depurar
  • Aplicar versionado básico para preparar tu API para el futuro
  • Tomar decisiones de diseño informadas respaldadas por razonamiento claro

Esta guía construye habilidades prácticas—serás capaz de diseñar APIs reales después de leerla.

1. La Base: Pensamiento Orientado a Recursos

Antes de escribir código, necesitas cambiar tu mentalidad de “endpoints que hacen cosas” a “recursos que existen.”

Piensa en Sustantivos, No Verbos

El error más común en diseño de APIs es crear endpoints orientados a acciones:

# MAL: Orientado a acciones (estilo RPC)
POST /createUser
POST /getUserById
POST /updateUserEmail
POST /deleteUser
POST /sendPasswordReset

Estos endpoints son verbos. Describen qué haces, no qué existe. Este enfoque lleva a APIs inconsistentes e impredecibles.

En cambio, piensa en recursos—las “cosas” en tu sistema:

# BIEN: Orientado a recursos (estilo REST)
POST   /users              # Crear un usuario
GET    /users/123          # Obtener usuario 123
PATCH  /users/123          # Actualizar usuario 123
DELETE /users/123          # Eliminar usuario 123
POST   /users/123/password-reset   # Disparar reseteo de contraseña

Los métodos HTTP ya transmiten la acción. Tus URLs deben identificar qué está siendo afectado.

Identificando Recursos

Los recursos típicamente son:

  • Entidades de negocio: Usuarios, productos, pedidos, facturas
  • Relaciones: Membresías, amistades, suscripciones
  • Acciones (cuando es necesario): Reseteos de contraseña, verificaciones de email

Pregúntate: “¿Cuáles son las cosas en mi sistema que tienen identidad y pueden ser manipuladas?”

graph TD
    A[Tu Dominio] --> B[Entidades de Negocio]
    A --> C[Relaciones]
    A --> D[Acciones como Recursos]
    B --> B1["users"]
    B --> B2["products"]
    B --> B3["orders"]
    C --> C1["memberships"]
    C --> C2["subscriptions"]
    D --> D1["password-resets"]
    D --> D2["email-verifications"]
    style A fill:#e3f2fd
    style B fill:#fff3e0
    style C fill:#fff3e0
    style D fill:#fff3e0

2. Diseño de URLs: El Arte de Nombrar

Las URLs son la interfaz de tu API. Deben ser predecibles, legibles y consistentes.

Usa Sustantivos en Plural

Las colecciones deben usar sustantivos en plural:

# BIEN: Sustantivos en plural
GET /users
GET /products
GET /orders

# MAL: Sustantivos en singular (inconsistente)
GET /user
GET /product
GET /order

Incluso para recursos singleton, considera el patrón de colección:

# Singleton en contexto de colección
GET /users/me          # Usuario actual
GET /settings/current  # Configuración actual

Jerarquía y Anidación

Los recursos frecuentemente tienen relaciones. Exprésalas a través de jerarquía de URLs:

# Pedidos de un usuario
GET /users/123/orders

# Pedido específico de un usuario
GET /users/123/orders/456

# Productos en un pedido
GET /users/123/orders/456/products

Pero no anides demasiado profundo. Más de 2-3 niveles se vuelve difícil de manejar:

# DEMASIADO PROFUNDO: Difícil de leer y usar
GET /companies/1/departments/2/teams/3/members/4/tasks/5

# MEJOR: Aplanar cuando sea posible
GET /tasks/5
GET /tasks?team_id=3

Regla general: Si un recurso tiene un identificador global, puede accederse directamente.

Convenciones de Nombres

ConvenciónEjemploCuándo Usar
Minúsculas/users, /productsSiempre
Guiones para múltiples palabras/order-items, /password-resetsSiempre
Plural para colecciones/users, /ordersPara colecciones
Sin barras finales/users no /users/Siempre
Sin extensiones de archivo/users no /users.jsonSiempre
Sin verbos en URLs/users no /getUsersSiempre (usa métodos HTTP)

Árbol de Decisiones para Diseño de URLs

flowchart TD
    A[Diseñar nuevo endpoint] --> B{¿Es una colección
de cosas?} B -->|Sí| C[Usa sustantivo plural:
/resources] B -->|No| D{¿Es un ítem
específico?} D -->|Sí| E[Usa colección + ID:
/resources/123] D -->|No| F{¿Está anidado
bajo otro recurso?} F -->|Sí| G{¿Profundidad > 2 niveles?} F -->|No| H[Crear como nivel superior:
/resources] G -->|Sí| I[Aplanarlo:
/child-resources?parent_id=X] G -->|No| J[Anidarlo:
/parents/X/children] style C fill:#c8e6c9 style E fill:#c8e6c9 style H fill:#c8e6c9 style I fill:#c8e6c9 style J fill:#c8e6c9

3. Colecciones e Ítems: Dos Patrones

Cada tipo de recurso típicamente expone dos patrones: la colección y el ítem.

Operaciones de Colección

Las colecciones representan grupos de recursos:

# Listar todos los usuarios (colección)
GET /users
→ Devuelve: Array de usuarios

# Crear un nuevo usuario (añadir a colección)
POST /users
→ Cuerpo: Datos del nuevo usuario
→ Devuelve: Usuario creado con ID

# Operaciones masivas (menos común)
DELETE /users?status=inactive
→ Elimina múltiples usuarios que coinciden con el criterio

Operaciones de Ítem

Los ítems representan recursos individuales:

# Obtener un usuario específico (ítem)
GET /users/123
→ Devuelve: Objeto de usuario único

# Actualizar un usuario específico (parcial)
PATCH /users/123
→ Cuerpo: Campos a actualizar
→ Devuelve: Usuario actualizado

# Reemplazar un usuario (completo)
PUT /users/123
→ Cuerpo: Objeto de usuario completo
→ Devuelve: Usuario reemplazado

# Eliminar un usuario específico
DELETE /users/123
→ Devuelve: 204 No Content

El Patrón CRUD Completo

OperaciónMétodo HTTPURLCuerpo de PeticiónRespuesta
Listar todosGET/users-Array de usuarios
Obtener unoGET/users/123-Usuario único
CrearPOST/usersNuevo usuarioUsuario creado + cabecera Location
Actualizar parcialPATCH/users/123Campos cambiadosUsuario actualizado
Reemplazar completoPUT/users/123Usuario completoUsuario reemplazado
EliminarDELETE/users/123-204 No Content

4. Paginación: Manejando Colecciones Grandes

Las colecciones del mundo real pueden tener millones de ítems. Necesitas paginación.

Tres Estrategias de Paginación

graph LR
    A[Estrategias de Paginación] --> B[Basada en offset]
    A --> C[Basada en cursor]
    A --> D[Basada en keyset]
    B --> B1["Simple, acceso aleatorio"]
    B --> B2["Lenta en datasets grandes"]
    C --> C1["Rápida, estable"]
    C --> C2["Sin acceso aleatorio"]
    D --> D1["Rápida, determinista"]
    D --> D2["Requiere campo ordenable"]
    style B fill:#fff9c4
    style C fill:#c8e6c9
    style D fill:#c8e6c9

Paginación Basada en Offset

El enfoque más simple. Especifica offset (saltar) y limit (tomar):

GET /users?offset=0&limit=20   # Página 1
GET /users?offset=20&limit=20  # Página 2
GET /users?offset=40&limit=20  # Página 3

Respuesta con metadatos de paginación:

{
  "data": [
    {"id": 1, "name": "Alice"},
    {"id": 2, "name": "Bob"}
  ],
  "pagination": {
    "offset": 0,
    "limit": 20,
    "total": 1547
  }
}

Pros:

  • Simple de implementar
  • Soporta acceso aleatorio (saltar a página 50)
  • Fácil de entender

Contras:

  • Lenta en datasets grandes (la base de datos debe contar las filas del offset)
  • Resultados inconsistentes cuando los datos cambian entre peticiones
  • El rendimiento degrada: OFFSET 1000000 es costoso

Usar cuando: Datasets pequeños (< 10,000 ítems), acceso aleatorio necesario.

Paginación Basada en Cursor

Usa un cursor opaco (marcador de posición codificado):

GET /users?limit=20
→ Devuelve: data + cursor

GET /users?limit=20&cursor=eyJpZCI6MjB9
→ Devuelve: siguiente página + nuevo cursor

Respuesta:

{
  "data": [
    {"id": 21, "name": "Carol"},
    {"id": 22, "name": "Dave"}
  ],
  "pagination": {
    "cursor": "eyJpZCI6NDB9",
    "has_more": true
  }
}

Pros:

  • Rápida en datasets grandes (sin contar offset)
  • Resultados estables (no saltará/duplicará cuando los datos cambien)
  • Funciona bien con datos en tiempo real

Contras:

  • Sin acceso aleatorio (debe recorrerse secuencialmente)
  • El cursor puede volverse inválido si el ítem referenciado se elimina
  • Más complejo de implementar

Usar cuando: Datasets grandes, UIs de scroll infinito, feeds en tiempo real.

Paginación Basada en Keyset

Usa el último valor visto de un campo ordenado:

GET /users?limit=20&sort=created_at
→ Devuelve: usuarios ordenados por created_at

GET /users?limit=20&sort=created_at&after=2026-01-01T12:00:00Z
→ Devuelve: usuarios creados después de esa marca de tiempo

Respuesta:

{
  "data": [
    {"id": 45, "name": "Eve", "created_at": "2026-01-01T12:00:01Z"},
    {"id": 46, "name": "Frank", "created_at": "2026-01-01T12:00:02Z"}
  ],
  "pagination": {
    "after": "2026-01-01T12:00:20Z",
    "has_more": true
  }
}

Pros:

  • Muy rápida (usa índices de base de datos directamente)
  • Resultados deterministas
  • No requiere conteo

Contras:

  • Requiere un campo único y ordenable
  • Sin acceso aleatorio
  • Complejo para ordenaciones multi-columna

Usar cuando: Datasets grandes ordenados por timestamp o ID.

Formato de Respuesta de Paginación

Siempre incluye metadatos de paginación:

{
  "data": [...],
  "pagination": {
    "total": 1547,
    "limit": 20,
    "offset": 40,
    "has_more": true,
    "links": {
      "self": "/users?offset=40&limit=20",
      "first": "/users?offset=0&limit=20",
      "prev": "/users?offset=20&limit=20",
      "next": "/users?offset=60&limit=20",
      "last": "/users?offset=1540&limit=20"
    }
  }
}

5. Filtrado: Encontrando Lo Que Necesitas

Las colecciones necesitan filtrado para ser útiles.

Sintaxis de Parámetros de Consulta

El enfoque más común usa parámetros de consulta:

# Filtros de igualdad simples
GET /users?status=active
GET /users?role=admin
GET /users?status=active&role=admin  # Lógica AND

# Múltiples valores (lógica OR)
GET /users?status=active,inactive
GET /users?role=admin&role=editor    # Parámetros múltiples

# Filtrado de campos anidados
GET /users?address.city=London

Operadores para Filtros Complejos

Para comparaciones más allá de la igualdad, usa sufijos de operador o sintaxis especial:

# Enfoque 1: Sufijos de operador
GET /products?price_gte=100           # price >= 100
GET /products?price_lte=500           # price <= 500
GET /products?created_at_gt=2026-01-01  # creado después de fecha

# Enfoque 2: Notación de corchetes
GET /products?price[gte]=100
GET /products?price[lte]=500
GET /products?created_at[gt]=2026-01-01

# Enfoque 3: Operadores especiales
GET /products?filter=price:gte:100
GET /products?filter=price:between:100,500

Operadores de Filtro Comunes

OperadorSignificadoEjemplo
eq (por defecto)Igual?status=active
neNo igual?status_ne=deleted
gtMayor que?price_gt=100
gteMayor o igual que?price_gte=100
ltMenor que?price_lt=500
lteMenor o igual que?price_lte=500
inEn lista?status_in=active,pending
containsCadena contiene?name_contains=john
startsCadena empieza con?name_starts=john

Búsqueda vs. Filtro

Distingue entre filtrado estructurado y búsqueda de texto completo:

# Filtro estructurado (coincidencia exacta de campo)
GET /products?category=electronics&brand=Sony

# Búsqueda de texto completo (busca en múltiples campos)
GET /products?q=wireless+headphones
GET /products?search=wireless+headphones

6. Ordenación: Ordenando Resultados

La ordenación controla el orden de los resultados de la colección.

Ordenación de Campo Único

# Ordenar por campo único
GET /users?sort=name           # Ascendente (por defecto)
GET /users?sort=name:asc       # Ascendente explícito
GET /users?sort=name:desc      # Descendente
GET /users?sort=-name          # Notación de prefijo: - significa desc

Ordenación Multi-Campo

# Múltiples campos de ordenación (separados por coma)
GET /users?sort=role:asc,name:asc
GET /users?sort=role,-created_at    # role asc, luego created_at desc

Cabeceras de Respuesta de Ordenación

Incluye info de ordenación en la respuesta:

{
  "data": [...],
  "sorting": {
    "fields": [
      {"field": "role", "direction": "asc"},
      {"field": "created_at", "direction": "desc"}
    ]
  }
}

Ordenación por Defecto

Siempre define un orden por defecto para consistencia:

# Si no se especifica ordenación
GET /users
→ Por defecto: sort=created_at:desc (más recientes primero)

# O: sort=id:asc (orden estable)

7. Respuestas de Error: Ayudando a Desarrolladores a Depurar

Las buenas respuestas de error hacen tu API más fácil de integrar.

Estructura de Error Consistente

Cada respuesta de error debe seguir la misma estructura:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "La petición contiene campos inválidos",
    "details": [
      {
        "field": "email",
        "code": "INVALID_FORMAT",
        "message": "El email debe ser una dirección de correo válida"
      },
      {
        "field": "age",
        "code": "OUT_OF_RANGE",
        "message": "La edad debe estar entre 18 y 120"
      }
    ],
    "request_id": "req_abc123xyz"
  }
}

Campos de Respuesta de Error

CampoPropósitoEjemplo
codeTipo de error legible por máquina"VALIDATION_ERROR", "NOT_FOUND"
messageExplicación legible por humanos"Usuario no encontrado"
detailsErrores a nivel de campo (para validación)Array de errores de campo
request_idPara depuración/soporte"req_abc123xyz"
documentation_urlEnlace a docs de ayuda (opcional)"https://api.example.com/docs/errors/RATE_LIMITED"

Categorías de Códigos de Error

Define un conjunto consistente de códigos de error:

# Autenticación/Autorización
AUTHENTICATION_REQUIRED    → 401
INVALID_CREDENTIALS       → 401
TOKEN_EXPIRED            → 401
PERMISSION_DENIED        → 403

# Errores de recurso
NOT_FOUND               → 404
ALREADY_EXISTS          → 409
CONFLICT                → 409

# Errores de validación
VALIDATION_ERROR        → 400
INVALID_FORMAT          → 400
MISSING_FIELD           → 400
OUT_OF_RANGE            → 400

# Límite de tasa
RATE_LIMITED            → 429

# Errores de servidor
INTERNAL_ERROR          → 500
SERVICE_UNAVAILABLE     → 503

Árbol de Decisiones de Error

flowchart TD
    A[Error ocurrido] --> B{¿Problema de
autenticación?} B -->|Sí| C{¿Token presente?} B -->|No| D{¿El recurso
existe?} C -->|No| E["401 + AUTHENTICATION_REQUIRED"] C -->|Sí| F{¿Token válido?} F -->|No| G["401 + INVALID_CREDENTIALS"] F -->|Sí| H["403 + PERMISSION_DENIED"] D -->|No| I["404 + NOT_FOUND"] D -->|Sí| J{¿Petición
válida?} J -->|No| K["400/422 + VALIDATION_ERROR"] J -->|Sí| L{¿Violación de regla
de negocio?} L -->|Sí| M["409 + CONFLICT o personalizado"] L -->|No| N["500 + INTERNAL_ERROR"] style E fill:#ffccbc style G fill:#ffccbc style H fill:#ffccbc style I fill:#ffccbc style K fill:#fff9c4 style M fill:#fff9c4 style N fill:#ffcdd2

Ejemplo de Error de Validación

Para validación de formularios/entrada, proporciona errores detallados a nivel de campo:

POST /users
Content-Type: application/json

{
  "name": "",
  "email": "not-an-email",
  "age": -5
}
HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "El cuerpo de la petición contiene campos inválidos",
    "details": [
      {
        "field": "name",
        "code": "REQUIRED",
        "message": "El nombre es requerido"
      },
      {
        "field": "email",
        "code": "INVALID_FORMAT",
        "message": "El email debe ser una dirección de correo válida"
      },
      {
        "field": "age",
        "code": "OUT_OF_RANGE",
        "message": "La edad debe ser un número positivo"
      }
    ]
  }
}

8. Selección de Campos: Respuestas Parciales

Permite a los clientes solicitar solo los campos que necesitan.

Conjuntos de Campos Dispersos

# Devolver solo campos específicos
GET /users/123?fields=id,name,email

# Respuesta
{
  "id": 123,
  "name": "Alice",
  "email": "[email protected]"
}

# Comparar con respuesta completa
GET /users/123

{
  "id": 123,
  "name": "Alice",
  "email": "[email protected]",
  "phone": "+1-555-1234",
  "address": {...},
  "preferences": {...},
  "created_at": "2026-01-01T00:00:00Z",
  "updated_at": "2026-01-10T12:00:00Z"
}

Selección de Campos Anidados

Para recursos relacionados:

# Seleccionar campos en objetos anidados
GET /users/123?fields=id,name,address.city,address.country

# Respuesta
{
  "id": 123,
  "name": "Alice",
  "address": {
    "city": "London",
    "country": "UK"
  }
}

Beneficios de la Selección de Campos

  • Reducción de ancho de banda: Envía solo lo necesario
  • Respuestas más rápidas: Menos datos que serializar
  • Amigable para móviles: Crítico para ancho de banda limitado
  • Privacidad: No exponer campos que el cliente no necesita

9. Versionado de API: Planificando para el Cambio

Las APIs evolucionan. El versionado te ayuda a hacer cambios sin romper clientes existentes.

¿Por Qué Versionar?

Sin versionado:

graph LR
    A[Cambio en API] --> B[Todos los Clientes Rotos]
    B --> C[Usuarios Enfadados]
    B --> D[Tickets de Soporte]
    B --> E[Pérdida de Confianza]
    style A fill:#ffccbc
    style B fill:#ffcdd2

Con versionado:

graph LR
    A[API v2 Lanzada] --> B[v1 Sigue Funcionando]
    A --> C[Clientes Migran
a Su Ritmo] B --> D[Sin Cambios que Rompen] C --> D D --> E[Usuarios Felices] style A fill:#c8e6c9 style D fill:#c8e6c9 style E fill:#c8e6c9

Versionado en Ruta de URL (Recomendado)

El enfoque más simple y visible:

# Versión en ruta de URL
GET /v1/users
GET /v2/users

# URL completa
https://api.example.com/v1/users
https://api.example.com/v2/users

Pros:

  • Altamente visible y explícito
  • Fácil de probar y depurar
  • Funciona con todas las herramientas HTTP
  • Claro en la documentación

Contras:

  • Cambia todas las URLs cuando la versión aumenta
  • Puede llevar a duplicación de código

Esta es la opción por defecto para la mayoría de APIs. Empieza aquí.

Versionado en Cabecera (Alternativa)

Versión especificada en cabecera de petición:

GET /users
Accept: application/vnd.example.v1+json

GET /users
Accept: application/vnd.example.v2+json

O cabecera personalizada:

GET /users
X-API-Version: 1

GET /users
X-API-Version: 2

Pros:

  • URLs limpias
  • Misma URL para todas las versiones
  • Sigue negociación de contenido HTTP

Contras:

  • Menos visible (no está en la URL)
  • Más difícil de probar en navegador
  • Fácil olvidar la cabecera

Versionado en Parámetro de Consulta (No Recomendado)

GET /users?version=1
GET /users?version=2

Evita este enfoque. Mezcla versionado con semántica de consulta y puede causar problemas de caché.

Cuándo Crear una Nueva Versión

Crea una nueva versión para cambios que rompen:

  • Eliminar un campo
  • Renombrar un campo
  • Cambiar el tipo de un campo
  • Cambiar estado requerido/opcional
  • Cambiar formato de respuesta de error
  • Cambiar esquema de autenticación

Cambios que no rompen no requieren nueva versión:

  • Añadir nuevos campos (aditivo)
  • Añadir nuevos endpoints
  • Añadir nuevos parámetros opcionales
  • Añadir nuevos códigos de error

Ciclo de Vida de Versión

v1 (actual) → v2 (lanzada) → v1 (deprecada) → v1 (sunset)
                              6-12 meses de aviso
                              v1 eliminada

Siempre anuncia la deprecación con antelación (6-12 meses mínimo para APIs de producción).

10. Poniéndolo Todo Junto: Ejemplo de API de E-Commerce

Diseñemos una API de e-commerce completa siguiendo todos los principios.

Jerarquía de Recursos

graph TD
    A[Raíz de API] --> B[/users]
    A --> C[/products]
    A --> D[/orders]
    A --> E[/categories]

    B --> B1[/users/:id]
    B1 --> B2[/users/:id/orders]
    B1 --> B3[/users/:id/addresses]

    C --> C1[/products/:id]
    C1 --> C2[/products/:id/reviews]

    D --> D1[/orders/:id]
    D1 --> D2[/orders/:id/items]

    E --> E1[/categories/:id]
    E1 --> E2[/categories/:id/products]

    style A fill:#e3f2fd

Diseño Completo de Endpoints

Usuarios:

GET    /v1/users                    # Listar usuarios (admin)
POST   /v1/users                    # Crear usuario (registro)
GET    /v1/users/me                 # Obtener usuario actual
PATCH  /v1/users/me                 # Actualizar usuario actual
GET    /v1/users/:id                # Obtener usuario por ID (admin)
GET    /v1/users/:id/orders         # Obtener pedidos del usuario
GET    /v1/users/:id/addresses      # Obtener direcciones del usuario
POST   /v1/users/:id/addresses      # Añadir dirección

Productos:

GET    /v1/products                 # Listar productos
GET    /v1/products/:id             # Obtener producto
POST   /v1/products                 # Crear producto (admin)
PATCH  /v1/products/:id             # Actualizar producto (admin)
DELETE /v1/products/:id             # Eliminar producto (admin)
GET    /v1/products/:id/reviews     # Obtener reseñas del producto
POST   /v1/products/:id/reviews     # Añadir reseña

Pedidos:

GET    /v1/orders                   # Listar pedidos (propios o admin)
POST   /v1/orders                   # Crear pedido
GET    /v1/orders/:id               # Obtener pedido
PATCH  /v1/orders/:id               # Actualizar pedido (cambios de estado)
DELETE /v1/orders/:id               # Cancelar pedido
GET    /v1/orders/:id/items         # Obtener ítems del pedido

Categorías:

GET    /v1/categories               # Listar categorías
GET    /v1/categories/:id           # Obtener categoría
GET    /v1/categories/:id/products  # Obtener productos en categoría

Ejemplo: Listar Productos con Filtros

GET /v1/products?category=electronics&price_gte=100&price_lte=500&sort=-rating&limit=20&offset=0

HTTP/1.1 200 OK
Content-Type: application/json

{
  "data": [
    {
      "id": "prod_123",
      "name": "Auriculares Inalámbricos",
      "category": "electronics",
      "price": 299.99,
      "rating": 4.8,
      "in_stock": true
    },
    {
      "id": "prod_456",
      "name": "Altavoz Bluetooth",
      "category": "electronics",
      "price": 149.99,
      "rating": 4.6,
      "in_stock": true
    }
  ],
  "pagination": {
    "offset": 0,
    "limit": 20,
    "total": 156,
    "has_more": true
  },
  "filters_applied": {
    "category": "electronics",
    "price_gte": 100,
    "price_lte": 500
  },
  "sorting": {
    "field": "rating",
    "direction": "desc"
  }
}

Ejemplo: Crear Pedido con Error

POST /v1/orders
Content-Type: application/json
Authorization: Bearer eyJhbG...

{
  "items": [
    {"product_id": "prod_123", "quantity": 0},
    {"product_id": "prod_999", "quantity": 2}
  ],
  "shipping_address_id": "addr_456"
}

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "No se pudo crear el pedido debido a errores de validación",
    "details": [
      {
        "field": "items[0].quantity",
        "code": "OUT_OF_RANGE",
        "message": "La cantidad debe ser al menos 1"
      },
      {
        "field": "items[1].product_id",
        "code": "NOT_FOUND",
        "message": "El producto 'prod_999' no existe"
      }
    ],
    "request_id": "req_xyz789"
  }
}

Qué Sigue

Esta guía cubrió los fundamentos del diseño de APIs REST—suficiente para construir una API bien estructurada.

Para temas más profundos como:

  • Evolución de API sin romper clientes
  • Estrategias de versionado para APIs en vivo
  • Patrones de migración para APIs existentes
  • Compatibilidad hacia atrás a largo plazo
  • Políticas de deprecación y sunset

Ve el próximo curso: Diseño de APIs REST que No Rompen.


Términos Relacionados del Vocabulario

Profundiza tu comprensión: