Saltar al contenido
Under the Hood
Available in Spanish only

APIs

API REST: no es solo mandar JSON, es cómo se manda

Última revisión:

General
  • Diseño de APIs
  • HTTP
  • Buenas prácticas

Respuesta corta

REST es un estilo de arquitectura, no un estándar: los recursos se identifican con URLs y se manipulan con los verbos HTTP según su semántica, y cada respuesta trae un código de estado que dice qué pasó.

Además es cliente-servidor, stateless y cacheable, lo que permite que cliente y servidor evolucionen por separado y que la API escale.

En esta página

Explicación

Un estilo, no un estándar

REST (Representational State Transfer) lo definió Roy Fielding en su tesis doctoral del año 2000. No es un protocolo ni un estándar: es un conjunto de restricciones de arquitectura que se apoya en estándares que sí existen, como HTTP, las URIs y los media types.

Por eso una API no es REST por usar JSON sobre HTTP, sino por respetar esas restricciones.

Las seis restricciones

Restricción Qué significa Por qué importa
Cliente-servidor Cliente y servidor están separados y solo se comunican a través de la API Evolucionan por separado: una misma API sirve a la web, al móvil y a otros backends
Stateless El servidor no guarda la sesión del cliente; cada request trae todo, incluida la autenticación Cualquier instancia atiende cualquier request, así que escala horizontalmente
Cacheable Cada respuesta declara si se puede cachear y por cuánto tiempo (Cache-Control, ETag) Menos carga en el servidor y respuestas más rápidas
Interfaz uniforme Recursos con URL, manipulados con representaciones y mensajes que se autodescriben Cualquier cliente entiende la API sin acuerdos especiales
Sistema en capas Puede haber proxies, gateways o una CDN en medio sin que el cliente lo note Se agrega caché, seguridad o balanceo sin tocar el cliente
Código bajo demanda (opcional) El servidor puede enviar código ejecutable al cliente Casi no se usa en APIs; es la única restricción opcional

Un matiz que vale la pena decir: stateless no significa sin base de datos. El servidor sí guarda el estado de los recursos (los pedidos, los usuarios). Lo que no guarda es el estado de la conversación con cada cliente.

Recursos, no acciones

La URL nombra una cosa y el verbo HTTP dice qué hacer con ella: GET /orders/42, no POST /getOrder. Las colecciones van en plural (/orders) y las relaciones se anidan (/orders/42/items).

Lo que viaja no es el recurso en sí, sino una representación suya, normalmente JSON. El header Content-Type indica el formato, así que el mismo recurso podría servirse en otro formato sin cambiar su URL.

Verbos y códigos de estado

Método Uso ¿Seguro? ¿Idempotente?
GET Leer Sí Sí
POST Crear No No
PUT Reemplazar completo No Sí
PATCH Modificar en parte No No necesariamente
DELETE Eliminar No Sí

Seguro significa que no cambia nada en el servidor. Idempotente, que repetir la request deja el mismo resultado que hacerla una vez. Esto importa en los reintentos: repetir un DELETE no rompe nada, pero repetir un POST puede crear dos pedidos.

La respuesta siempre dice qué pasó con su código de estado: 2xx es éxito, 4xx es un error del cliente y 5xx es un error del servidor. Los que más espero ver en una API:

  • 200 OK, 201 Created (con el header Location del recurso nuevo) y 204 No Content.
  • 400 Bad Request, 404 Not Found y 409 Conflict.
  • 401 Unauthorized (no sé quién eres) frente a 403 Forbidden (sé quién eres, pero no tienes permiso).
  • 500 Internal Server Error.

Código

El mismo recurso de pedidos, primero como RPC disfrazado de REST y después respetando las reglas. Fíjate en las URLs, los verbos y los códigos de estado.

Antes (RPC)

pedidos-rpc.http
### Crear un pedido
POST /createOrder HTTP/1.1
Content-Type: application/json
{ "productId": 7, "quantity": 2 }
HTTP/1.1 200 OK
Content-Type: application/json
{ "ok": true, "orderId": 42 }
### Consultar un pedido que no existe
POST /getOrder HTTP/1.1
Content-Type: application/json
{ "orderId": 99 }
HTTP/1.1 200 OK
Content-Type: application/json
{ "ok": false, "error": "Order not found" }

Después (REST)

pedidos-rest.http
### Crear un pedido
POST /orders HTTP/1.1
Content-Type: application/json
{ "productId": 7, "quantity": 2 }
HTTP/1.1 201 Created
Location: /orders/42
### Leer un pedido
GET /orders/42 HTTP/1.1
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: max-age=60
ETag: "a1b2c3"
{ "id": 42, "productId": 7, "quantity": 2, "status": "pending" }
### Eliminar un pedido
DELETE /orders/42 HTTP/1.1
HTTP/1.1 204 No Content
### Leer un pedido que no existe
GET /orders/99 HTTP/1.1
HTTP/1.1 404 Not Found

Trade-offs

  • Simplicidad frente a flexibilidad. REST usa HTTP tal cual, así que funciona con cualquier cliente y aprovecha la caché y la infraestructura que ya existen. A cambio, la forma de la respuesta la decide el servidor, lo que puede traer datos de más o de menos y obligar a varias llamadas. Para esos casos existen alternativas como GraphQL o gRPC, cada una con sus propios costos.
  • Stateless escala, pero cada request carga su contexto. El token viaja y se valida en cada llamada, y eso tiene un costo en tamaño y en procesamiento.
  • La caché acelera, pero hay que invalidarla. Si Cache-Control y ETag no están bien pensados, el cliente puede ver datos obsoletos.
  • El error típico: RPC disfrazado de REST. Verbos en la URL, todo por POST y 200 con el error en el cuerpo. Funciona, pero pierde la caché, la idempotencia y los códigos de estado que cualquier cliente o proxy sabe interpretar.
  • Dato adicional: HATEOAS. Es la restricción que casi nadie cumple: que la respuesta incluya enlaces a las acciones posibles, para que el cliente navegue la API sin conocer las URLs de antemano.

Entradas relacionadas

  • Cloud y DevOps

    ¿Sabes armar un buen pipeline de CI? Pasos y consideraciones

    Un proceso de CI corre en cada push o pull request y responde una pregunta: ¿este cambio se puede integrar a la rama principal sin romper nada? Para eso restaura dependencias, corre el lint, compila, ejecuta los tests y revisa vulnerabilidades, de lo más rápido a lo más lento. Si todo pasa, genera un artefacto versionado y lo publica; si algo falla, se detiene y el PR queda bloqueado. Desplegar ese artefacto ya es CD.

    General