Maestría en Desarrollo Full Stack Moderno
Backend con Node y Express
Cimientos del backend moderno
Construir un backend sólido es como diseñar los cimientos de un rascacielos. Si la base es débil, todo lo demás se vendrá abajo. Para nuestras aplicaciones MERN, esa base la construiremos con Node.js y Express.js, creando una API RESTful.
Una API RESTful es esencialmente un conjunto de reglas que permite que diferentes aplicaciones se comuniquen entre sí a través de HTTP, el protocolo de la web. Tu frontend (React) le pedirá datos al backend (Node/Express), y este le responderá, generalmente en formato JSON. Nos saltaremos los conceptos básicos de JavaScript y nos centraremos directamente en las prácticas profesionales que definen el desarrollo backend en 2025.
Estructura profesional del proyecto
Antes de escribir una sola línea de lógica, organicemos nuestro espacio de trabajo. Una estructura de carpetas bien definida no es una formalidad, es la clave para que una aplicación sea escalable y fácil de mantener a largo plazo. Adoptaremos el patrón "Controlador-Ruta", que separa las responsabilidades de forma clara y lógica.
mi-proyecto/
├── node_modules/
├── src/
│ ├── controllers/
│ │ └── user.controller.js
│ ├── middlewares/
│ │ └── errorHandler.js
│ ├── routes/
│ │ └── user.routes.js
│ └── app.js
├── .gitignore
├── package.json
Así se desglosa esta estructura:
controllers: Aquí vive la lógica de negocio. Cada función aquí (un controlador) se encarga de una tarea específica, como crear un usuario o buscar un producto.routes: Define los puntos de entrada (endpoints) de tu API. Asocia una URL y un método HTTP (como GET o POST) con el controlador que debe manejar esa petición.middlewares: Son funciones que se ejecutan entre la petición del cliente y la respuesta del servidor. Se usan para autenticación, registro de logs, validación y manejo de errores.app.js: Es el corazón de tu aplicación. Configura Express, conecta los middlewares y las rutas, e inicia el servidor.
Una buena arquitectura separa las preocupaciones. Las rutas escuchan, los controladores actúan y los middlewares preparan el terreno.
El flujo de una petición y los middlewares
Imagina una petición HTTP como un paquete que viaja por una cadena de montaje. Cada estación en la cadena es un middleware. Cada middleware puede inspeccionar el paquete (req), modificarlo, o incluso rechazarlo antes de que llegue a su destino final (el controlador). Express se basa por completo en este concepto.
Un middleware es simplemente una función con tres parámetros: req (el objeto de la petición), res (el objeto de la respuesta) y next (una función que pasa el control al siguiente middleware en la cadena).
// src/app.js
import express from 'express';
import userRoutes from './routes/user.routes.js';
import { errorHandler } from './middlewares/errorHandler.js';
const app = express();
// Middleware para parsear JSON. Sin esto, req.body sería undefined.
app.use(express.json());
// Un middleware personalizado para registrar la hora de cada petición
app.use((req, res, next) => {
console.log(`Petición recibida a las: ${new Date().toISOString()}`);
next(); // ¡Crucial! Pasa el control al siguiente middleware/ruta.
});
// Usamos nuestras rutas definidas
app.use('/api/users', userRoutes);
// Middleware de manejo de errores. Se coloca al final.
app.use(errorHandler);
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Servidor corriendo en el puerto ${PORT}`);
});
Fíjate en el orden. Express ejecuta los middlewares en la secuencia en que se declaran con app.use(). El middleware express.json() es fundamental para cualquier API que reciba datos en formato JSON. Nuestro logger personalizado se ejecuta después, y finalmente, las peticiones que empiezan con /api/users se dirigen a nuestro enrutador de usuarios.
Validación y manejo de errores
Nunca confíes en los datos que llegan del cliente. La validación es tu primera línea de defensa contra datos incorrectos o maliciosos. Bibliotecas como Zod o express-validator nos permiten definir esquemas claros para los datos que esperamos recibir. Si una petición no cumple con el esquema, la rechazamos antes de que llegue a nuestra lógica de negocio.
Zod es una opción moderna y popular por su excelente integración con TypeScript y su sintaxis declarativa. Aquí vemos cómo se podría usar en un middleware de validación:
// Un validador de esquema usando Zod
import { z } from 'zod';
const createUserSchema = z.object({
body: z.object({
username: z.string().min(3, 'El nombre de usuario debe tener al menos 3 caracteres'),
email: z.string().email('Debe ser un email válido'),
password: z.string().min(8, 'La contraseña debe tener al menos 8 caracteres'),
}),
});
// Middleware para usar el esquema
const validate = (schema) => (req, res, next) => {
try {
schema.parse({
body: req.body,
query: req.query,
params: req.params,
});
next();
} catch (error) {
res.status(400).json({ errors: error.errors });
}
};
// En user.routes.js
// router.post('/', validate(createUserSchema), userController.createUser);
Pero, ¿qué pasa si un error ocurre en las profundidades de un controlador? No queremos que la aplicación se caiga. Implementamos un gestor de errores global. Este es un tipo especial de middleware que tiene cuatro parámetros (err, req, res, next). Se coloca al final de la pila de middlewares en app.js, como vimos antes. Atrapa cualquier error que ocurra en la aplicación y nos permite enviar una respuesta HTTP estandarizada y limpia.
// src/middlewares/errorHandler.js
export const errorHandler = (err, req, res, next) => {
console.error(err.stack);
const statusCode = err.statusCode || 500;
const message = err.message || 'Ha ocurrido un error interno en el servidor.';
res.status(statusCode).json({
status: 'error',
statusCode,
message,
});
};
Este enfoque centralizado mantiene nuestro código limpio. Los controladores ya no necesitan bloques try...catch repetitivos. Simplemente lanzan un error, y nuestro errorHandler se encargará de formatear y enviar la respuesta adecuada.
¿Cuál es la función principal de la carpeta controllers en una aplicación Express.js que sigue el patrón "Controlador-Ruta"?
En un middleware de Express, ¿cuál es el propósito de la función next()?
Con estos pilares, ya tienes una arquitectura de backend robusta, escalable y profesional, lista para conectar una base de datos y construir funcionalidades complejas.