From n00b to ZeroCool / Profesionalización

REST: el idioma universal de las APIs (sin hacerlo religión)

REST explicado con ejemplos reales: recursos, métodos HTTP, status codes, errores, versionado, paginación e idempotencia para backend.

Lo que vale la pena leer aquí

Resultado: caches rotos, retries infinitos, y el backend haciendo talacha de más. Y lo peor: en el dashboard se veía como tráfico normal de lectura. Nadie lo cachó hasta que tronó.

Cuando production se cae y “nomás era un GET”

Son las 7:58 a.m. en CDMX. Tu laptop ya pide jubilación, el standup arranca en dos minutos y te cae el mensaje clásico: “Oye, la app no carga, sale 500”. Te metes a los logs con tu café de Oxxo y ves la joya: un endpoint “de lectura” que alguien usó para actualizar cosas “porque era más fácil”.

Resultado: caches rotos, retries infinitos, y el backend haciendo talacha de más. Y lo peor: en el dashboard se veía como tráfico normal de lectura. Nadie lo cachó hasta que tronó.

REST no te salva de todo, pero sí te quita un montón de autogoles cuando tu API crece, cambia de manos y la terminan usando apps, partners, scripts… y el Excel con macros del área de ventas.

Qué te vas a llevar

  • Pensar en recursos (y por qué eso ordena el stack).
  • Mapear CRUD a HTTP sin inventarte verbos raros.
  • Status codes que sí dicen la neta (y ayudan a debuggear en corto).
  • Convenciones que sí pegan en el jale: paginación, filtros, versionado, errores consistentes.
  • Tradeoffs reales: cuándo REST te queda chico y cómo no sufrir.

REST no es “hacer endpoints”, es diseñar un contrato

REST se volvió “idioma universal” porque usa cosas que ya vienen en el kit de Internet:

  • HTTP (métodos, headers, status codes)
  • URLs como identificadores de cosas (recursos)
  • Representaciones (casi siempre JSON)
  • Semántica estándar: GET lee, POST crea, PUT reemplaza, PATCH modifica, DELETE borra

La parte que importa: si respetas esa semántica, tu API se vuelve predecible. Y lo predecible se monitorea mejor, se documenta más fácil y se integra más rápido.

El cambio mental clave: aquí no piensas “funciones remotas”. Piensas cosas.

  • Recurso: users, orders, invoices, tickets.
  • No recurso: getUserData, doPayment, setStatus (eso es RPC con gabardina).

Cuando entra alguien nuevo al repo (o tú mismo tres meses después), una API REST decente se lee como mapa.

Guía principal: arma una API REST que no te dé pena en 6 meses

1) Empieza por el dominio: recursos y relaciones

Ejemplo e-commerce (porque aunque digas que no, la vida te pone uno):

  • users
  • products
  • carts
  • orders
  • payments

Relaciones típicas:

  • Un user tiene muchos orders.
  • Un order tiene muchos items.

Rutas comunes:

  • /users
  • /users/{userId}
  • /users/{userId}/orders
  • /orders/{orderId}
  • /orders/{orderId}/items

Regla que evita broncas: sustantivos en el path, verbos en el método.

2) Mapea métodos HTTP a acciones (sin hackear GET)

Un set sano:

  • Listar: GET /products
  • Detalle: GET /products/{id}
  • Crear: POST /products
  • Reemplazar: PUT /products/{id}
  • Cambio parcial: PATCH /products/{id}
  • Borrar: DELETE /products/{id}

Lo que he visto mil veces con prisa y deadline encima: GET /orders/close?id=123 “porque el front lo hace fácil”.

  • Te rompe caches (CDN/proxies asumen que GET es safe).
  • Te ensucia métricas (parece lectura en tus dashboards).
  • Te abre riesgos raros (prefetch del navegador, crawlers, links compartidos).

Si cambia estado, casi siempre debería ser POST/PATCH.

3) Define contratos JSON consistentes (no improvises cada response)

Ejemplo de GET /products/42:

{
  "id": 42,
  "name": "Teclado mecánico",
  "price": 1299.0,
  "currency": "MXN",
  "stock": 7,
  "createdAt": "2026-07-16T12:00:00Z",
  "updatedAt": "2026-07-16T12:10:00Z"
}

Cicatrices de producción:

  • Fechas en ISO 8601. Nada de “16/07/26” porque luego nadie sabe si es 16 de julio o 7 de diciembre.
  • Moneda explícita si vendes en LatAm. Los bugs de moneda son silenciosos y carísimos.
  • No mezcles camelCase y snake_case. Elige uno y mantenlo, aunque te dé toc.

4) Status codes que te ahorran horas de debug

Piénsalos como semáforos para humanos y tooling.

Los que sí vas a usar:

  • 200 OK: todo bien
  • 201 Created: creaste algo (ideal: Location)
  • 204 No Content: actualización/borrado sin body
  • 400 Bad Request: el cliente mandó basura (validación básica)
  • 401 Unauthorized: no autenticado (token faltante o inválido)
  • 403 Forbidden: autenticado pero sin permisos
  • 404 Not Found: no existe (o no quieres revelar que existe)
  • 409 Conflict: duplicados, versiones, conflicto de estado
  • 422 Unprocessable Entity: validación semántica (si tu equipo la maneja)
  • 429 Too Many Requests: rate limit
  • 500/502/503: broncas del servidor / upstream / indisponibilidad

Ejemplo real: crear una orden con 201.

Request:

POST /orders
Content-Type: application/json
Authorization: Bearer <token>

{
  "items": [{"productId": 42, "qty": 2}],
  "paymentMethod": "card"
}

Response:

HTTP/1.1 201 Created
Location: /orders/8f3a2c
Content-Type: application/json

{
  "id": "8f3a2c",
  "status": "created",
  "total": 2598.0,
  "currency": "MXN"
}

5) Errores con formato estándar (soporte te lo va a agradecer)

El error más caro es el que no puedes diagnosticar.

Formato simple y útil:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "El campo qty debe ser mayor a 0",
    "details": [{"field": "items[0].qty", "issue": "min"}],
    "traceId": "9c1b7f1e2b0a"
  }
}

Decisión práctica: siempre regresa un traceId o requestId y loguéalo. Cuando te escriban por Slack “no sirve”, pides ese ID y ya no estás adivinando.

6) Paginación, filtros y orden: el trípode para que no muera tu DB

Si tienes listas, tarde o temprano cae el ticket: “la pantalla se tarda 12 segundos”.

Paginación clásica con limit/offset:

  • GET /products?limit=20&offset=40

Para feeds grandes, cursor-based:

  • GET /products?limit=20&cursor=eyJpZCI6NDJ9

Filtros y orden:

  • GET /orders?status=paid&sort=-createdAt

Response paginada con metadata:

{
  "data": [{"id": "8f3a2c", "status": "paid"}],
  "pagination": {
    "limit": 20,
    "nextCursor": "eyJpZCI6OTl9"
  }
}

Tradeoff que importa:

  • offset es fácil, pero se degrada en tablas grandes.
  • cursor es más estable, pero te pide más setup y disciplina.
REST: el idioma universal de las APIs (sin hacerlo religión) - visual explicativa 1
Visual de apoyo: Cuando production se cae y “nomás era un GET”

7) Versionado sin drama: no rompas a tus consumidores

Lo más común (y práctico) es versionar en la URL:

  • /v1/orders
  • /v2/orders

También existe por header (Accept: application/vnd...), pero en equipos mixtos suele meter fricción y bugs tontos.

Regla del backend underworld: si ya hay clientes en production, romper contrato cuesta. A veces ni te enteras hasta que el partner te manda una captura borrosa de Postman.

Estrategia que funciona:

  • Cambios compatibles: agrega campos nuevos y documenta.
  • Cambios incompatibles: nueva versión o endpoint alterno.
  • Deprecación: marca como deprecated y pon fecha. Si no hay fecha, nadie lo arregla.

8) Idempotencia y retries: cuando el Internet falla (porque siempre falla)

Entre red en el metro, WiFi de coworking y timeouts random, los clientes reintentan.

  • GET, PUT, DELETE deberían ser idempotentes.
  • POST normalmente no, pero puedes hacerlo con Idempotency-Key.

Ejemplo:

POST /payments
Idempotency-Key: 6b1e2a7f-9c2a-4b9b-a2d5-0a0f2a
Content-Type: application/json

{"orderId":"8f3a2c","amount":2598.0}

Si llega dos veces, regresas el mismo resultado sin cobrar doble. En pagos, eso es la diferencia entre “se arregla” y “te cae un rollback con llamadas de cliente”.

9) Autenticación y autorización: REST no lo trae incluido

REST es estilo, no framework. Para auth lo típico:

  • JWT Bearer tokens
  • OAuth2 (si hay terceros)
  • API keys (server-to-server)

Decisiones rápidas:

  • App propia: JWT + refresh tokens bien hechos.
  • Partners: API keys con rate limit + scopes.

Y sí: valida permisos en backend aunque el front “oculte botones”. Esa película ya la vimos.

Screenshots sugeridos

  • Captura de Postman/Insomnia mostrando:
    • GET /products?limit=20&offset=0
    • Response con data y pagination
  • Captura de logs (o APM tipo Datadog/New Relic) donde se vea un traceId correlacionando request y error.
  • Captura de un archivo openapi.yaml o Swagger UI con endpoints versionados /v1.

Errores comunes (los que sí pasan) + solución

  1. Verbos en la URL (/getUser, /updateStatus)

    • Solución: modela recursos y usa métodos HTTP. Si es una acción especial, usa subrecurso tipo POST /orders/{id}/cancel.
  2. Regresar 200 para todo (incluyendo errores)

    • Solución: status codes correctos + body de error consistente. Te mejora monitoreo, alertas y experiencia del cliente.
  3. Renombrar campos “porque se ven mejor”

    • Solución: versiona o agrega campos nuevos. Renombrar sin plan rompe apps viejas en silencio.
  4. Sin paginación (y el endpoint se trae 30k filas)

    • Solución: paginación obligatoria en listas; defaults (limit=20) y máximo definido.
  5. No diferenciar 401 vs 403

    • Solución: 401 = no autenticado; 403 = sin permisos. En soporte eso acelera el diagnóstico cañón.
REST: el idioma universal de las APIs (sin hacerlo religión) - visual explicativa 2
Visual de apoyo: Qué te vas a llevar

Checklist final (para tu próxima API o refactor)

  • Rutas con sustantivos: recursos claros y consistentes.
  • GET no cambia estado (nada de “GET que actualiza”).
  • Status codes alineados a lo que pasó.
  • Formato de error estándar con traceId.
  • Paginación + filtros + orden definidos.
  • Versionado explícito (al menos /v1).
  • Idempotencia considerada para operaciones críticas (pagos/creación).
  • Authn/Authz implementado en backend (no confiar en el front).
  • Documentación mínima (OpenAPI/Swagger) con ejemplos de request/response.

FAQ

1) ¿REST es lo mismo que JSON sobre HTTP?

No. JSON sobre HTTP puede ser cualquier cosa. REST implica recursos, métodos con semántica, status codes y un contrato predecible.

2) ¿Tengo que usar PUT o con PATCH basta?

PATCH suele ser más práctico para cambios parciales. PUT sirve si vas a reemplazar el recurso completo y quieres idempotencia más clara. Elige uno y sé consistente.

3) ¿Cuándo uso 404 vs 403?

Si el usuario no debería ni saber que existe el recurso, muchas veces regresas 404. Si sí existe pero no tiene permiso (y no hay problema en decirlo), 403.

4) ¿Cómo manejo “acciones” como cancelar un pedido o resetear password?

Si es una acción con efectos, usa POST /orders/{id}/cancel o POST /users/{id}/password-reset. No es “REST académico”, pero es claro y mantenible.

5) ¿REST o GraphQL?

REST va perfecto para CRUD, integraciones y caching sencillo. GraphQL brilla cuando el front necesita flexibilidad de queries. En equipos chicos, REST suele ganar por simplicidad operativa.

Siguiente episodio

Si REST es el idioma, el diccionario es tu contrato: OpenAPI/Swagger.
Vamos a armar documentación que no dé flojera y que sí sirva para onboarding, mocks y tests.