Hoy · Hoja de ruta
- 1. Aprende: Cómo se decide qué hacer según el método y la ruta, y qué estado responder.
- 2. Haz: Sirve tu catálogo en
/api/productos, leyendo y creando. - 3. Comprueba: Cada situación devuelve el código de estado correcto.
Antes de empezar · 5 minutos, sin apuntes
- ¿Qué significan 200, 201, 400, 404 y 500?
- ¿Qué método usarías para crear algo? ¿Y para consultarlo?
- Si el cliente envía datos inválidos, ¿de quién es el fallo?
Enrutar a mano
const servidor = http.createServer(async (peticion, respuesta) => {
const url = new URL(peticion.url, `http://${peticion.headers.host}`);
const ruta = url.pathname;
if (peticion.method === "GET" && ruta === "/api/productos") {
return responderJson(respuesta, 200, await listar());
}
if (peticion.method === "GET" && ruta.startsWith("/api/productos/")) {
const id = Number(ruta.split("/").pop());
const producto = await obtener(id);
if (!producto) return responderJson(respuesta, 404, { error: "No encontrado" });
return responderJson(respuesta, 200, producto);
}
responderJson(respuesta, 404, { error: "Ruta no encontrada" });
});
Se ve venir el problema: con quince rutas esto es una escalera de condicionales, y cada ruta con parámetro exige partir el texto a mano. Guárdalo en la memoria para la sesión 13.
Leer el cuerpo de una petición
async function leerCuerpo(peticion) {
const trozos = [];
for await (const trozo of peticion) trozos.push(trozo);
const texto = Buffer.concat(trozos).toString("utf8");
return texto === "" ? null : JSON.parse(texto);
}
El cuerpo llega a trozos
El cuerpo no es una propiedad legible de una sola vez, sino un flujo que llega por fragmentos. Por eso hay que acumular los trozos y solo entonces convertirlos a texto y analizarlos.
Y ese JSON.parse es un dato de fuera: un cuerpo mal formado lanza una excepción que, sin capturar, tumba la petición con un 500 cuando en realidad el fallo es del cliente y merece un 400.
Los códigos que vas a usar
| Código | Cuándo |
|---|---|
| 200 | Todo bien, aquí está |
| 201 | Creado; con la cabecera Location |
| 204 | Todo bien, no hay nada que devolver |
| 400 | La petición está mal formada o los datos no son válidos |
| 401 / 403 | No autenticado / autenticado pero sin permiso |
| 404 | El recurso no existe |
| 405 | El método no está permitido en esta ruta |
| 409 | Conflicto: ya existe algo así |
| 500 | Se ha roto algo en el servidor |
La familia del código dice de quién es el problema
Los 4xx significan «lo has pedido mal»; los 5xx, «se me ha roto a mí». Devolver 200 con un cuerpo que dice «error» rompe esa convención, y cualquier cliente automático —incluido tu propio fetch de la UD4, que mira respuesta.ok— se lo creerá.
Un 500 en tus registros es una tarea pendiente para ti. Un 400 es información para quien llama.
Un formato de error constante
{ "error": "Producto no encontrado", "detalles": [] }
Que todas las respuestas de error tengan la misma forma permite al cliente escribir un solo tratamiento. Es un contrato, y romperlo a mitad de una API es una fuente inagotable de fallos en el cliente.
Tarea 11 · La API a mano
- Implementa
GET /api/productoscon filtro por categoría en la consulta. - Implementa
GET /api/productos/:idcon su 404. - Implementa
POST /api/productoscon validación, 201 y cabeceraLocation. - Devuelve 400 con la lista de errores cuando la validación falle.
- Responde 405 si el método no está soportado en una ruta que sí existe.
- Escribe un fichero
peticiones.httpque pruebe los seis casos.
Checkpoint · fin de la sesión 11
- Decides la acción por método y ruta.
- Lees el cuerpo acumulando el flujo.
- Devuelves el código de estado que corresponde a cada caso.
- Todos tus errores tienen la misma forma.
Antes de cerrar · 2 minutos, sin mirar
- ¿Qué diferencia hay entre un 400 y un 500?
- ¿Qué se devuelve al crear un recurso?
- ¿Por qué el cuerpo se lee por trozos?
Ver respuestas
1 · El 400 dice que la petición estaba mal; el 500, que ha fallado el servidor.
2 · Un 201 con la cabecera Location apuntando al recurso creado.
3 · Porque llega como un flujo, no como un valor ya disponible.