← Node.js: JavaScript fuera del navegador

Sesión 10 · Semana 4

Un servidor HTTP con las manos

Hoy · Hoja de ruta

  1. 1. Aprende: Qué hace exactamente un servidor web y cómo se escribe uno con el módulo nativo.
  2. 2. Haz: Levanta tu primer servidor y respóndele al navegador.
  3. 3. Comprueba: Sabes leer una petición y componer una respuesta completa.

Antes de empezar · 5 minutos, sin apuntes

  1. Cuando en la UD4 hacías fetch, ¿qué le llegaba al otro lado?
  2. ¿Qué partes tiene una petición HTTP? ¿Y una respuesta?
  3. ¿Por qué crees que un servidor «se queda escuchando»?

Diez líneas

import http from "node:http";

const servidor = http.createServer((peticion, respuesta) => {
  respuesta.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
  respuesta.end("Hola desde mi servidor");
});

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

Eso es un servidor web. Arráncalo, abre el navegador y ahí está: por primera vez, el otro lado es tuyo.

Lo que ocurre en cada visita
  1. El navegador abre una conexión al puerto 3000
  2. Envía la petición: método, ruta, cabeceras y quizá cuerpo
  3. Node ejecuta tu función
  4. Tu función escribe el estado, las cabeceras y el cuerpo
  5. Se cierra la respuesta y el navegador la pinta

La petición

peticion.method;      // "GET", "POST"…
peticion.url;         // "/api/productos?categoria=teclados"
peticion.headers;     // un objeto con las cabeceras

const url = new URL(peticion.url, `http://${peticion.headers.host}`);
url.pathname;                       // "/api/productos"
url.searchParams.get("categoria");  // "teclados"

peticion.url trae la ruta y la cadena de consulta juntas y sin analizar. URL las separa, y searchParams da los parámetros ya decodificados: es la misma clase que existe en el navegador.

La respuesta

respuesta.statusCode = 404;
respuesta.setHeader("Content-Type", "application/json; charset=utf-8");
respuesta.end(JSON.stringify({ error: "No encontrado" }));

Toda respuesta se cierra, y solo una vez

Si tu función termina sin llamar a end, el navegador permanece a la espera hasta agotar el tiempo límite. Si se invoca dos veces —típico de un if sin return— Node lanza un error de cabeceras ya enviadas.

Escribe siempre return después de responder. Es la costumbre que te ahorrará las dos caras del mismo problema.

El puerto

const PUERTO = Number(process.env.PUERTO ?? 3000);

Si al arrancar ves EADDRINUSE, el puerto está ocupado: casi siempre por otro servidor tuyo que se quedó corriendo. O lo paras, o arrancas en otro puerto.

Tarea 10 · Tu primer servidor

  1. Escribe src/servidor.js que responda texto en cualquier ruta.
  2. Registra por consola método, ruta y momento de cada petición.
  3. Responde de forma distinta a / y a /hola.
  4. Lee un parámetro de la cadena de consulta y devuélvelo.
  5. Comprueba todo con el navegador y con tu cliente HTTP.
  6. Provoca a propósito una respuesta sin cerrar y observa qué hace el navegador.
Objetivo mínimoServidor levantado, dos rutas y registro de peticiones.
Si lo tienesDevuelve JSON con la información completa de la petición.
RetoArranca dos servidores en el mismo puerto y explica el error.

Checkpoint · fin de la sesión 10

  • Tienes un servidor que arranca y responde.
  • Lees método, ruta y parámetros.
  • Escribes estado, cabeceras y cuerpo.
  • Cierras siempre la respuesta, y solo una vez.

Antes de cerrar · 2 minutos, sin mirar

  1. ¿Qué tres partes escribes en una respuesta?
  2. ¿Qué significa EADDRINUSE?
  3. ¿Qué pasa si no cierras la respuesta?
Ver respuestas

1 · El código de estado, las cabeceras y el cuerpo.

2 · Que el puerto ya está ocupado por otro proceso.

3 · El cliente espera hasta agotar el tiempo y no recibe nada.