← Node & Express

Fundamentos de Express

¿Qué información traen los objetos `req` y `res` en Express y cuáles son los métodos de respuesta más comunes?

Ver respuesta — intenta responderla en voz alta primero

En cada handler (req, res), req (request) representa lo que envía el cliente y res (response) es lo que tú le devuelves. De req lees method, url, params, query, body y headers. Con res respondes usando métodos como res.json() (envía JSON), res.send() (envía texto/HTML/JSON), res.status() (fija el código HTTP) y res.end() (cierra sin cuerpo). res.status() devuelve el mismo res, así que puedes encadenar: res.status(201).json(...).

El objeto req contiene todo lo que llegó del cliente:

  • req.method: el verbo HTTP, "GET", "POST", etc.
  • req.url: la URL pedida, incluyendo el query string.
  • req.params: los route params de la URL (/usuarios/:idreq.params.id).
  • req.query: lo que viene después del ? (?page=2req.query.page).
  • req.body: el cuerpo enviado (JSON del POST/PUT). Necesita express.json().
  • req.headers: las cabeceras (req.headers["content-type"], etc.).

El objeto res sirve para construir la respuesta. Métodos clave:

  • res.json(obj): envía obj como JSON y pone el Content-Type correcto.
  • res.send(algo): envía texto, HTML o incluso un objeto (lo convierte a JSON).
  • res.status(codigo): fija el código de estado (200, 201, 404, 500...). No envía nada por sí solo, solo prepara el código; devuelve res para encadenar.
  • res.end(): termina la respuesta sin cuerpo (útil para un 204 No Content).

Regla de oro: por cada petición debes responder una sola vez. Enviar dos respuestas (por ejemplo un res.json y luego otro) tira un error.

const express = require("express");
const app = express();

// Necesario para poder leer req.body en formato JSON
app.use(express.json());

// Muestra la info que trae req
app.get("/inspeccionar/:id", (req, res) => {
  // Leemos varias piezas del request
  const info = {
    metodo: req.method,            // "GET"
    url: req.url,                  // "/inspeccionar/7?debug=true"
    parametro: req.params.id,      // "7" (de la ruta :id)
    query: req.query,              // { debug: "true" } (después del ?)
    userAgent: req.headers["user-agent"], // cabecera enviada por el cliente
  };

  // res.json envía el objeto como JSON con status 200 por defecto
  res.json(info);
});

// Ejemplo de encadenamiento: fijar status y enviar JSON en una línea
app.post("/usuarios", (req, res) => {
  const nuevo = { id: 1, nombre: req.body.nombre };

  // status(201) prepara el código y devuelve res; json envía el cuerpo
  res.status(201).json(nuevo);
});

// res.send con texto plano
app.get("/saludo", (req, res) => {
  res.send("Hola mundo"); // Content-Type: text/html automático
});

// res.status + res.end: responder sin cuerpo (204 = sin contenido)
app.delete("/usuarios/:id", (req, res) => {
  // ...aquí borrarías el usuario...
  res.status(204).end();
});

app.listen(3000, () => {
  console.log("Servidor en http://localhost:3000");
});

Probando con curl:

# Pasamos un route param (7) y un query (?debug=true)
curl "http://localhost:3000/inspeccionar/7?debug=true"

# POST con body JSON; deberías recibir 201 y el usuario creado
curl -X POST http://localhost:3000/usuarios \
  -H "Content-Type: application/json" \
  -d '{"nombre":"Ana"}'

Creer que res.status(404) por sí solo envía la respuesta. No es así: res.status() solo fija el código y devuelve res; tienes que encadenar un método que envíe cuerpo, como res.status(404).json({ error: "No encontrado" }) o res.status(404).end(). Si solo llamas res.status(404), la petición se queda colgada porque nunca se cerró. Otro error es responder dos veces (res.json(...) y después otro res.send(...)), lo que lanza "Cannot set headers after they are sent".

"En cada handler tengo req y res. req es lo que manda el cliente: de ahí leo method, url, params para los segmentos de la ruta, query para lo que viene tras el ?, body para el cuerpo y headers para las cabeceras. res es lo que devuelvo: uso res.json para mandar JSON, res.send para texto o HTML, res.status para fijar el código y res.end para cerrar sin cuerpo. Algo clave: res.status no envía nada solo, solo fija el código y devuelve res, por eso encadeno res.status(201).json(...). Y siempre respondo una sola vez por petición, porque enviar dos respuestas tira el error de headers ya enviados."

Reto rápido

¿Qué problema tiene este handler y cómo lo arreglas?

app.get("/usuarios/:id", (req, res) => {
  const usuario = buscarUsuario(req.params.id);
  if (!usuario) {
    res.status(404);
  }
  res.json(usuario);
});
Ver respuesta

res.status(404) no envía respuesta, solo fija el código; el flujo continúa y llega a res.json(usuario), que enviará undefined como JSON... con status 200, no 404. La condición no cortó nada.

Se arregla enviando el cuerpo dentro del if y frenando la función con return:

app.get("/usuarios/:id", (req, res) => {
  const usuario = buscarUsuario(req.params.id);
  if (!usuario) {
    return res.status(404).json({ error: "Usuario no encontrado" });
  }
  res.json(usuario);
});

El return evita que se ejecute el res.json de abajo y que se intente responder dos veces.