¿Qué diferencia a una API REST bien diseñada de una mediocre?
Las buenas APIs REST se leen como un mapa del dominio: sustantivos para los recursos, verbos HTTP para las acciones, y códigos de estado que no mienten.
APRENDE HACIENDOPon a prueba tu intuición3 pasos
¿Qué diferencia a una API REST bien diseñada de una mediocre?
Usa los ejemplos y la explicación de la lección para comprobar tu idea.
Reflexión personal. No se envía a ningún servicio de IA.#Sustantivos para los recursos, verbos para las acciones
Una API REST es, en el fondo, una promesa sobre cuánto puede predecir un cliente su forma. Si conocés el sustantivo (/users, /orders, /invoices) y conocés los seis o siete verbos que te da HTTP, deberías poder adivinar casi toda la API sin leer una sola página de documentación. Esa previsibilidad es el verdadero valor del estilo "REST" — todo lo demás (statelessness, cacheabilidad, una interfaz uniforme) existe para proteger esa promesa a medida que el sistema crece.
Las APIs mediocres rompen la promesa de una manera muy específica: meten el verbo dentro de la URL. /getAllUsers, /createUser, /deleteUserById son llamadas RPC disfrazadas de HTTP. No hay nada malo con RPC — gRPC es RPC y es excelente — pero si vas a llamar "REST" a algo, el recurso va en el sustantivo y la acción va en el método HTTP. En el momento en que una URL contiene un verbo, duplicaste la superficie de tu API sin necesidad: ahora cada acción futura necesita su propio verbo artesanal, en vez de reutilizar los mismos siete que HTTP ya estandarizó, cacheó y documentó en cada cliente HTTP del planeta.
#Un antes y un después
Acá está el mismo conjunto de funcionalidades — usuarios, y los pedidos de un usuario — diseñado de dos formas.
GET /getAllUsersPOST /createNewUserGET /getUserOrders?userId=42POST /deleteUser?id=42GET /user-order-detail/42/99
Y la versión con forma REST:
GET /users # listar usuariosPOST /users # crear un usuarioGET /users/42 # leer un usuarioPATCH /users/42 # actualizar parcialmente un usuarioDELETE /users/42 # eliminar un usuarioGET /users/42/orders # listar los pedidos de un usuario (recurso anidado)GET /orders/99 # leer un pedido directamente, por su propio id
Fijate que /orders/99 también existe por sí solo, fuera del anidamiento bajo /users. Es deliberado: un pedido tiene identidad y ciclo de vida propios, independientes del cliente que lo generó — soporte, facturación y el depósito necesitan llegar a él directamente. Un error típico de junior es anidar todo bajo el recurso padre y obligar a cada consumidor a conocer el id del padre solo para llegar al hijo.
#Los códigos de estado son parte del contrato, no decoración
Una API bien diseñada trata el código de estado HTTP como información estructural, no como un sello de 200 en cada respuesta con la respuesta real escondida en un campo success del JSON.
| Situación | Estado correcto | Lo que hace una API mediocre |
|---|---|---|
| Recurso creado | 201 Created + header Location | 200 OK con { "success": true } |
| Falló la validación | 422 Unprocessable Entity o 400 Bad Request | 200 OK con { "error": "..." } |
| No autenticado | 401 Unauthorized | 403 Forbidden (semántica incorrecta) o 200 con error en el body |
| Sin permiso | 403 Forbidden | 404 Not Found (a veces intencional por seguridad, pero casi siempre un bug) |
| Recurso inexistente | 404 Not Found | 200 OK con null |
| Actualización exitosa, nada que devolver | 204 No Content | 200 OK con un objeto vacío |
#Paginación, filtrado y orden son parte del diseño del recurso
Un endpoint de listado que devuelve "todos" es una API bien diseñada para una demo y una caída de producción en el mundo real. GET /users sobre una tabla con diez millones de filas necesita un contrato de paginación (?page=3&perPage=50 o, mejor a escala, paginación por cursor ?cursor=eyJpZCI6NDJ9), de filtrado (?status=active) y de orden (?sort=-createdAt). No son detalles que se agregan después: diseñar el recurso sin ellos es la elección mediocre, porque agregarle paginación a un endpoint ya publicado, cacheado y documentado es un cambio incompatible.
{"data": [ { "id": 41, "name": "Ada" }, { "id": 42, "name": "Grace" } ],"page": { "cursor": "eyJpZCI6NDJ9", "hasMore": true, "total": 128340 }}
#El árbol de recursos, visualmente
Dos caminos llegan al mismo recurso de pedido — a través del usuario que lo generó, y directamente por su propio id. Un buen diseño REST está cómodo con ese tipo de grafo; no fuerza un único árbol.