Node.js permite ejecutar JavaScript fuera del navegador, y eso incluye montar servidores web. Podrías escribir un servidor usando solo el módulo nativo http de Node, pero en cuanto necesitas más de dos rutas el código se vuelve repetitivo rápido. Ahí es donde entra Express: un framework minimalista que lleva más de una década siendo el estándar de facto para construir servidores y APIs en Node.

Si vienes de montar tu primer proyecto de frontend, este artículo es el complemento natural de tu primera app con React desde cero: aquí construyes el lado del servidor que esa app consumiría. Y si aún no tienes tus proyectos versionados, te conviene revisar antes nuestra guía de Git y GitHub para principiantes, porque vas a querer guardar este servidor en un repositorio desde el primer commit.

Instala Node.js y crea el proyecto

Necesitas tener Node.js instalado (la versión LTS es la recomendable para casi cualquier proyecto). Con eso listo, crea una carpeta para el proyecto e inicializa un package.json:

bash
mkdir mi-servidor
cd mi-servidor
npm init -y
npm install express

El package.json guarda las dependencias del proyecto y, con el flag -y, acepta todos los valores por defecto sin preguntarte nada. Con Express instalado, ya puedes crear el archivo principal del servidor.

Tu primer servidor: "hola mundo"

Un servidor Express en su forma mínima necesita tres cosas: crear la aplicación, definir al menos una ruta y ponerla a escuchar en un puerto.

javascript
// servidor.js
const express = require("express");
const app = express();
const PUERTO = 3000;

app.get("/", (req, res) => {
  res.send("Servidor funcionando");
});

app.listen(PUERTO, () => {
  console.log(`Servidor escuchando en http://localhost:${PUERTO}`);
});

Ejecuta node servidor.js y visita http://localhost:3000 en el navegador. Cada app.get(), app.post(), etc. define una ruta: una combinación de método HTTP y URL a la que Express responde ejecutando la función que le pasas.

Rutas GET y POST: la base de cualquier API

La mayoría de APIs REST giran en torno a un puñado de operaciones sobre un recurso: leer datos (GET) y crear datos nuevos (POST), además de actualizar (PUT/PATCH) y borrar (DELETE). Mira cómo se ven una ruta GET que devuelve una lista y una ruta POST que añade un elemento nuevo, usando un array en memoria como "base de datos" de ejemplo:

javascript
let tareas = [
  { id: 1, texto: "Aprender Express" },
  { id: 2, texto: "Montar la API" }
];

app.get("/tareas", (req, res) => {
  res.json(tareas);
});

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.json(tarea);
});
javascript
app.use(express.json()); // necesario para leer JSON en el body

app.post("/tareas", (req, res) => {
  const { texto } = req.body;
  if (!texto) {
    return res.status(400).json({ error: "El campo 'texto' es obligatorio" });
  }
  const nuevaTarea = { id: Date.now(), texto };
  tareas.push(nuevaTarea);
  res.status(201).json(nuevaTarea);
});

req.params te da los valores capturados en la URL (como el :id), mientras que req.body te da los datos que el cliente envió en el cuerpo de la petición, normalmente en formato JSON. Los códigos de estado importan: 200 para éxito, 201 cuando creas algo, 400 si la petición está mal formada y 404 si el recurso no existe. Un cliente que consuma tu API depende de esos códigos tanto como del contenido de la respuesta.

Middleware: el corazón de Express

Un middleware es una función que se ejecuta entre que llega la petición y se genera la respuesta. Puede modificar la petición, cortar el flujo (por ejemplo, si no hay autenticación) o simplemente registrar información y dejar pasar la petición al siguiente paso con next(). De hecho, express.json() que usaste arriba es un middleware: intercepta la petición, parsea el body como JSON y lo deja disponible en req.body.

javascript
function logger(req, res, next) {
  const ahora = new Date().toISOString();
  console.log(`[${ahora}] ${req.method} ${req.url}`);
  next(); // sin esto, la petición se queda colgada
}

app.use(logger);

// Middleware de manejo de errores (siempre con 4 argumentos)
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({ error: "Algo falló en el servidor" });
});

El orden importa: los middleware se ejecutan en el orden en que los defines con app.use(), y un middleware de manejo de errores como el del ejemplo debe ir al final, después de todas las rutas. Olvidar llamar a next() dentro de un middleware es uno de los errores más comunes al empezar: la petición simplemente se queda esperando una respuesta que nunca llega.

Organiza las rutas cuando el proyecto crece

Meter todas las rutas en un único archivo funciona para un ejemplo, pero no escala. Express permite agrupar rutas relacionadas con express.Router() y montarlas bajo un prefijo común, lo que mantiene el código organizado por recurso (tareas, usuarios, productos...).

javascript
// rutas/tareas.js
const express = require("express");
const router = express.Router();

router.get("/", (req, res) => res.json(tareas));
router.post("/", (req, res) => { /* ... */ });

module.exports = router;

// servidor.js
const rutasTareas = require("./rutas/tareas");
app.use("/tareas", rutasTareas);
Un servidor Express no necesita frameworks adicionales para empezar. Domina rutas, middleware y códigos de estado antes de añadir una base de datos o un sistema de autenticación completo.

Qué viene después

Con rutas, middleware y una API básica ya tienes lo esencial. Los siguientes pasos naturales son conectar una base de datos real en lugar del array en memoria, añadir validación más estricta de los datos de entrada y, si vas a exponer la API a producción, revisar buenas prácticas de seguridad: entender el OWASP Top 10 te ayudará a evitar los errores de seguridad más frecuentes en APIs recién estrenadas, desde la falta de validación hasta la exposición accidental de datos sensibles en las respuestas.

JR

Javier Rodríguez Fernández

Programador con Grado Superior en Desarrollo de Aplicaciones Multiplataforma (DAM). Escribe en DevPulso sobre programación aplicada, sin relleno.