← Node & Express

APIs REST

¿Qué familias de códigos de estado HTTP existen y qué código usarías en cada operación CRUD?

Ver respuesta — intenta responderla en voz alta primero

Los códigos se agrupan en familias según el primer dígito: 2xx (éxito), 3xx (redirección), 4xx (error del cliente) y 5xx (error del servidor). En CRUD lo típico es: 200 al leer, 201 al crear, 204 al borrar sin devolver contenido, 400/422 cuando el cliente manda datos inválidos, 404 cuando el recurso no existe y 500 cuando algo falla en el servidor.

El código de estado le dice al cliente cómo terminó su petición. Se agrupan en familias:

  • 2xx – Éxito. La petición se procesó bien.
    • 200 OK: todo bien, y normalmente hay un cuerpo de respuesta (leer o actualizar con éxito).
    • 201 Created: se creó un recurso nuevo (típico de un POST).
    • 204 No Content: todo bien, pero no hay cuerpo que devolver (típico de un DELETE).
  • 3xx – Redirección. El recurso está en otro sitio o no ha cambiado.
    • 301 Moved Permanently: el recurso se movió a otra URL de forma permanente.
    • 304 Not Modified: el recurso no ha cambiado desde la última vez (usado con caché).
  • 4xx – Error del cliente. La petición está mal hecha; el problema es del que llama.
    • 400 Bad Request: petición mal formada (JSON roto, falta un campo obligatorio).
    • 401 Unauthorized: falta autenticación o el token es inválido (no sé quién eres).
    • 403 Forbidden: estás autenticado pero no tienes permiso (sé quién eres, pero no puedes).
    • 404 Not Found: el recurso no existe.
    • 422 Unprocessable Entity: la sintaxis es correcta pero los datos son semánticamente inválidos (por ejemplo, un email con formato imposible).
  • 5xx – Error del servidor. La petición estaba bien, pero el servidor falló.
    • 500 Internal Server Error: error inesperado en el servidor.
    • 503 Service Unavailable: el servidor no está disponible ahora (sobrecarga o mantenimiento).

Regla mental rápida: 4xx = culpa del cliente, 5xx = culpa del servidor.

Qué código usar en cada operación CRUD

Operación Verbo HTTP Éxito Errores frecuentes
Create (crear) POST 201 Created 400 / 422 (datos inválidos)
Read (leer) GET 200 OK 404 (no existe)
Update (actualizar) PUT / PATCH 200 OK 400 / 422, 404
Delete (borrar) DELETE 204 No Content 404 (no existe)
const express = require('express');
const app = express();
app.use(express.json());

let tareas = [{ id: 1, titulo: 'Estudiar HTTP' }];

// READ -> 200 si existe, 404 si no
app.get('/tareas/:id', (req, res) => {
  const tarea = tareas.find((t) => t.id === Number(req.params.id));
  if (!tarea) {
    return res.status(404).json({ error: 'Tarea no encontrada' });
  }
  res.status(200).json(tarea);
});

// CREATE -> 400 si falta el título, 201 si se crea
app.post('/tareas', (req, res) => {
  if (!req.body.titulo) {
    return res.status(400).json({ error: 'El campo "titulo" es obligatorio' });
  }
  const nueva = { id: tareas.length + 1, titulo: req.body.titulo };
  tareas.push(nueva);
  res.status(201).json(nueva);
});

// DELETE -> 404 si no existe, 204 si se borra (sin cuerpo)
app.delete('/tareas/:id', (req, res) => {
  const indice = tareas.findIndex((t) => t.id === Number(req.params.id));
  if (indice === -1) {
    return res.status(404).json({ error: 'Tarea no encontrada' });
  }
  tareas.splice(indice, 1);
  res.status(204).send(); // 204 no lleva cuerpo
});

app.listen(3000);
# Crear una tarea (esperamos 201)
curl -i -X POST http://localhost:3000/tareas \
  -H "Content-Type: application/json" \
  -d '{"titulo":"Repasar códigos HTTP"}'

# Pedir una tarea que no existe (esperamos 404)
curl -i http://localhost:3000/tareas/999

Devolver 200 para todo, incluso cuando hay errores, y meter el estado real dentro del cuerpo JSON ({ "ok": false }). Esto obliga al cliente a "adivinar" leyendo el body y rompe herramientas y clientes que confían en el código HTTP. Otro error clásico es confundir 401 (no estás autenticado) con 403 (estás autenticado pero no tienes permiso), y usar 200 en una creación en lugar de 201.

"Los códigos HTTP se agrupan por el primer dígito: 2xx es éxito, 3xx redirección, 4xx error del cliente y 5xx error del servidor. La regla mental que uso es que 4xx es culpa de quien llama y 5xx es culpa del servidor. En un CRUD normal devuelvo 200 al leer o actualizar, 201 al crear porque nació un recurso nuevo, y 204 al borrar cuando no hay nada que devolver. Para errores del cliente uso 400 cuando la petición está mal formada, 422 cuando la estructura es válida pero los datos no tienen sentido, 401 cuando falta autenticación, 403 cuando está autenticado pero sin permiso, y 404 cuando el recurso no existe. Y reservo 500 para fallos inesperados del servidor. Lo importante es no devolver siempre 200 y esconder el error dentro del JSON."

Reto rápido

Un cliente hace POST /pedidos con un cuerpo JSON válido pero el campo total viene en negativo, algo que tu negocio no permite. ¿Qué código de estado devolverías y por qué?

Ver respuesta

422 Unprocessable Entity. El JSON está bien formado sintácticamente (por eso no es un 400 estricto), pero el contenido es semánticamente inválido: un total negativo no tiene sentido según las reglas del negocio. El 422 comunica justo eso: "te entendí, pero estos datos no los puedo procesar".

(Muchas APIs también aceptan un 400 aquí; lo importante es explicar la diferencia entre "mal formado" y "semánticamente inválido".)