Configuración del Proyecto Node.js y Express
En esta lección aprenderás a configurar un proyecto profesional de Node.js y Express desde cero. Veremos cómo organizar la estructura de directorios, instalar las dependencias necesarias y configurar el entorno de desarrollo para construir APIs RESTful robustas y mantenibles.
Requisitos Previos
Antes de comenzar, asegúrate de tener instalado lo siguiente en tu sistema:
- Node.js versión 18.x o superior (incluye npm)
- Visual Studio Code o cualquier editor de código moderno
- Postman o Insomnia para probar endpoints
- Conceptos básicos de JavaScript y programación asíncrona
node --version y npm --version en tu terminal. Ambas versiones deben mostrarse sin errores.Inicialización del Proyecto
- Crear el directorio del proyecto: Abre tu terminal y ejecuta
mkdir mi-api-rest && cd mi-api-rest - Inicializar package.json: Ejecuta
npm inity sigue las instrucciones del asistente interactivo - Instalar Express y dependencias:
npm install express cors helmet morgan dotenv - Instalar dependencias de desarrollo:
npm install --save-dev nodemon eslint - Crear la estructura de carpetas: Organiza tu código siguiendo las mejores prácticas
Estructura del Proyecto
Una estructura de directorios bien definida es crucial para proyectos de mediana y gran escala. Te recomiendo la siguiente organización:
mi-api-rest/
├── src/
│ ├── config/ # Configuración de la aplicación
│ │ └── index.js
│ ├── controllers/ # Lógica de los endpoints
│ │ └── userController.js
│ ├── middleware/ # Funciones intermedias
│ │ ├── errorHandler.js
│ │ └── validator.js
│ ├── models/ # Definición de esquemas
│ │ └── User.js
│ ├── routes/ # Definición de rutas
│ │ └── userRoutes.js
│ ├── services/ # Lógica de negocio
│ │ └── userService.js
│ ├── utils/ # Funciones auxiliares
│ │ └── helpers.js
│ └── app.js # Punto de entrada principal
├── .env # Variables de entorno
├── .env.example # Plantilla de variables
├── .gitignore # Archivos ignorados por Git
├── package.json
└── README.md
Configuración del Archivo package.json
Después de ejecutar npm init, tu archivo package.json básico se verá así:
{
"name": "mi-api-rest",
"version": "1.0.0",
"description": "API RESTful con Node.js y Express",
"main": "src/app.js",
"scripts": {
"start": "node src/app.js",
"dev": "nodemon src/app.js",
"test": "jest",
"lint": "eslint src/"
},
"keywords": ["api", "rest", "nodejs", "express"],
"author": "Tu Nombre",
"license": "MIT"
}
.env a tu repositorio Git. Este archivo contiene información sensible como claves de API, contraseñas de base de datos y tokens de acceso. Utiliza siempre .gitignore para excluirlo.Instalación de Dependencias
Dependencias de Producción
| Paquete | Propósito | Versión recomendada |
|---|---|---|
express |
Framework web principal | ^4.18.2 |
cors |
Manejo de Cross-Origin Resource Sharing | ^2.8.5 |
helmet |
Seguridad mediante headers HTTP | ^7.1.0 |
morgan |
Logging de peticiones HTTP | ^1.10.0 |
dotenv |
Variables de entorno | ^16.3.1 |
express-validator |
Validación de datos de entrada | ^7.0.1 |
Dependencias de Desarrollo
| Paquete | Propósito |
|---|---|
nodemon |
Reinicio automático del servidor durante desarrollo |
eslint |
Análisis estático de código JavaScript |
prettier |
Formato automático de código |
Configuración del Archivo de Entrada
Ahora crearemos el archivo principal src/app.js con todas las configuraciones esenciales:
// src/app.js
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const errorHandler = require('./middleware/errorHandler');
const app = express();
const PORT = process.env.PORT || 3000;
// Middlewares de seguridad
app.use(helmet());
// Middleware CORS
app.use(cors({
origin: process.env.CORS_ORIGIN || '*',
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
allowedHeaders: ['Content-Type', 'Authorization']
}));
// Logging de peticiones
app.use(morgan('combined'));
// Parsing de JSON
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true }));
// Rutas de la API
app.get('/api/v1/health', (req, res) => {
res.status(200).json({
status: 'success',
message: 'API funcionando correctamente',
timestamp: new Date().toISOString()
});
});
// Manejo de rutas no encontradas
app.use((req, res) => {
res.status(404).json({
status: 'error',
message: `Ruta ${req.originalUrl} no encontrada`
});
});
// Middleware de manejo de errores
app.use(errorHandler);
// Inicio del servidor
app.listen(PORT, () => {
console.log(`🚀 Servidor ejecutándose en http://localhost:${PORT}`);
console.log(`📚 Documentación: http://localhost:${PORT}/api/v1/docs`);
});
module.exports = app;
La modularización no es solo una buena práctica, es una necesidad. Un código bien organizado permite que múltiples desarrolladores trabajen simultáneamente sin conflictos.
Configuración de Variables de Entorno
Crea un archivo .env en la raíz del proyecto:
# Entorno
NODE_ENV=development
# Servidor
PORT=3000
# CORS
CORS_ORIGIN=http://localhost:5173
# Base de datos (para lecciones futuras)
DB_HOST=localhost
DB_PORT=5432
DB_NAME=mi_api_db
DB_USER=postgres
DB_PASSWORD=tu_contraseña_segura
# JWT (para lecciones futuras)
JWT_SECRET=tu_secreto_muy_largo_y_aleatorio
JWT_EXPIRES_IN=7d
crypto.randomBytes(64).toString('hex') en Node.js para generar secretos seguros.Middleware de Manejo de Errores
El manejo de errores es fundamental para cualquier API profesional. Crea src/middleware/errorHandler.js:
// src/middleware/errorHandler.js
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error';
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
const errorHandler = (err, req, res, next) => {
err.statusCode = err.statusCode || 500;
err.status = err.status || 'error';
// Respuesta de error
res.status(err.statusCode).json({
status: err.status,
message: err.message,
...(process.env.NODE_ENV === 'development' && {
error: err,
stack: err.stack
})
});
};
module.exports = { AppError, errorHandler };
Ver más: ¿Por qué usar clases de error personalizadas?
Las clases de error personalizadas como AppError nos permiten:
- Diferenciar entre errores operativos (esperados) y errores de programación (bugs)
- Incluir códigos de estado HTTP de forma consistente
- Marcar errores como "operacionales" para filtrado en logging
- Mantener un formato de respuesta uniforme para todos los errores
En producción, los errores operativos deben mostrarse al cliente, mientras que los errores de programación deben registrarse internamente sin exponer detalles sensibles.
Configuración de Scripts npm
Añade scripts útiles a tu package.json:
"scripts": {
"start": "node src/app.js",
"dev": "nodemon src/app.js",
"prod": "NODE_ENV=production node src/app.js",
"test": "jest --coverage",
"test:watch": "jest --watch",
"lint": "eslint src/ --fix",
"format": "prettier --write \"src/**/*.js\"",
"prepare": "husky install"
}
Configuración de ESLint
Crea un archivo .eslintrc.json para mantener la consistencia del código:
{
"env": {
"node": true,
"es2021": true,
"jest": true
},
"extends": ["eslint:recommended"],
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"rules": {
"no-unused-vars": "warn",
"no-console": "off",
"consistent-return": "error",
"prefer-const": "error"
}
}
npm install --save-dev husky lint-staged y configura los hooks apropiadamente.Prueba de la Configuración
Verifica que todo funcione ejecutando:
npm run dev
Deberías ver en tu terminal:
[nodemon] starting `node src/app.js`
🚀 Servidor ejecutándose en http://localhost:3000
📚 Documentación: http://localhost:3000/api/v1/docs
Ahora, en otra terminal o en tu navegador, prueba el endpoint de salud:
curl http://localhost:3000/api/v1/health
Deberías recibir:
{
"status": "success",
"message": "API funcionando correctamente",
"timestamp": "2024-01-15T10:30:00.000Z"
}
Ctrl+C antes de continuar. Cada vez que hagas cambios en el código, nodemon reiniciará automáticamente el servidor.Resumen de lo Aprendido
En esta lección hemos cubierto los fundamentos esenciales para configurar un proyecto profesional de Node.js y Express:
- ✓ Estructura de directorios escalable y mantenible
- ✓ Configuración de
package.jsoncon scripts útiles - ✓ Instalación de middleware de seguridad y utilidad
- ✓ Configuración de variables de entorno con
dotenv - ✓ Middleware personalizado para manejo de errores
- ✓ Configuración de herramientas de desarrollo (ESLint, Prettier)
- ✓ Verificación de que el servidor funciona correctamente
¿Cuál es el propósito principal del middleware helmet en una API Express?
- A) Aumentar la velocidad del servidor
- B) Configurar automáticamente headers HTTP de seguridad
- C) Validar los datos de entrada del usuario
- D) Conectar con bases de datos
helmet establece automáticamente diversos headers HTTP relacionados con la seguridad para proteger la aplicación contra ataques comunes como XSS, clickjacking, y otras vulnerabilidades web.Logging Avanzado con Winston
Para aplicaciones en producción, considera reemplazar morgan con winston, que ofrece más flexibilidad:
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' })
]
});
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({
format: winston.format.simple()
}));
}
Rate Limiting
Protege tu API contra ataques de denegación de servicio y abuso:
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutos
max: 100, // límite de 100 peticiones por IP
message: 'Demasiadas peticiones desde esta IP, intenta más tarde'
});
app.use('/api', limiter);
En la siguiente lección, profundizaremos en la creación de rutas y controladores para tu API RESTful, implementando operaciones CRUD completas con validación de datos.