Preparar el proyecto para producción
En desarrollo solemos priorizar rapidez (recarga automática, logs verbosos, errores detallados). En producción el objetivo cambia: estabilidad, seguridad, observabilidad y arranque reproducible. Para ello, tu API debe poder iniciarse con un comando claro, escuchar en un puerto configurable y comportarse distinto según el entorno (por ejemplo, menos detalle de errores).
Scripts de build/start en package.json
Un despliegue básico suele apoyarse en dos scripts: uno para construir (si aplica) y otro para arrancar. En una API Express “pura” normalmente no hay build, pero si usas TypeScript o un bundler, sí.
{
"scripts": {
"dev": "nodemon src/server.js",
"start": "node src/server.js",
"start:prod": "NODE_ENV=production node src/server.js"
}
}Si tu proyecto usa TypeScript:
{
"scripts": {
"build": "tsc -p tsconfig.json",
"start": "node dist/server.js",
"start:prod": "NODE_ENV=production node dist/server.js"
}
}En servidores Windows, la asignación de variables tipo NODE_ENV=production puede variar. En despliegues reales, lo habitual es que el proveedor (o tu servicio) inyecte variables de entorno y tú solo ejecutes npm run start.
Configuración de PORT
En producción casi nunca eliges un puerto fijo “a mano”. El entorno (PaaS, contenedor, VM con reverse proxy) suele definirlo. Tu app debe leerlo de process.env.PORT y tener un valor por defecto para local.
- Escuche el audio con la pantalla apagada.
- Obtenga un certificado al finalizar.
- ¡Más de 5000 cursos para que explores!
Descargar la aplicación
const express = require('express');
const app = express();
const PORT = Number(process.env.PORT) || 3000;
app.listen(PORT, () => {
console.log(`API escuchando en puerto ${PORT}`);
});Evita “hardcodear” el puerto en producción. Si el servidor te asigna PORT=8080 y tu app insiste en 3000, el despliegue fallará.
Diferencias clave entre dev y prod
| Aspecto | Dev | Prod |
|---|---|---|
| Arranque | Con recarga (nodemon) | Node directo o process manager |
| Logs | Muy verbosos | Estructurados, nivel controlado |
| Errores | Detalles para depurar | Mensajes seguros, sin filtrar internals |
| Configuración | .env local | Variables del servidor/servicio |
| Escalado | 1 proceso | 1+ procesos, balanceo/reverse proxy |
La regla práctica: en producción, asume que tu proceso puede reiniciarse en cualquier momento y que tus logs serán tu principal herramienta para entender qué pasó.
Concepto: proceso Node y cómo se ejecuta tu API
Cuando ejecutas node src/server.js, creas un proceso del sistema operativo que corre tu aplicación. Ese proceso:
- Consume memoria y CPU.
- Mantiene conexiones abiertas (HTTP, base de datos).
- Puede terminar por errores no controlados, falta de memoria, reinicio del servidor, etc.
En producción, necesitas decidir cómo mantener ese proceso “vivo”: ejecución directa (simple) o con un administrador de procesos (más robusto).
Guía práctica: despliegue básico paso a paso
Paso 1: asegurar un arranque predecible
Verifica que tu app arranca con npm run start sin depender de herramientas de desarrollo. Comprueba también que el puerto se toma de process.env.PORT.
- Local:
PORT=3000 npm run start - Servidor: el entorno define
PORTy ejecutasnpm run start
Paso 2: definir variables de entorno en el servidor
En producción, evita subir tu archivo .env al repositorio. Lo típico es configurar variables en el panel del proveedor o en el servicio que arranca tu app (systemd, PM2, Docker, etc.).
Ejemplo de variables comunes:
NODE_ENV=productionPORT=8080DATABASE_URL=...LOG_LEVEL=info
Tu código debe leerlas desde process.env y comportarse acorde (por ejemplo, menos detalle en errores si NODE_ENV es production).
Paso 3: elegir ejecución directa o process manager
Ejecución directa (simple)
La forma más simple es ejecutar Node directamente. Es útil para pruebas o entornos muy controlados:
npm ci
npm run startLimitación: si el proceso se cae, no se reinicia solo (a menos que el sistema lo haga por ti).
Process manager básico con PM2
PM2 es una herramienta común para mantener procesos Node en ejecución: reinicia si hay fallos, permite ver logs y gestionar instancias. Sin entrar en administración avanzada, un flujo básico sería:
npm install -g pm2
pm2 start src/server.js --name my-api
pm2 status
pm2 logs my-apiPara pasar variables de entorno al proceso, puedes definirlas en el entorno del servidor antes de arrancar, o usar un archivo de ecosistema (opcional). Ejemplo mínimo:
// ecosystem.config.js
module.exports = {
apps: [
{
name: 'my-api',
script: 'src/server.js',
env: {
NODE_ENV: 'production',
PORT: 8080
}
}
]
};pm2 start ecosystem.config.js
pm2 logs my-apiPM2 también puede ejecutar varias instancias (modo cluster) para aprovechar múltiples núcleos, pero en un despliegue básico puedes empezar con una sola instancia y escalar después.
Reverse proxy (concepto) y por qué se usa
En muchos despliegues, tu app Express no se expone directamente a internet. En su lugar, un reverse proxy (por ejemplo Nginx o un balanceador del proveedor) recibe las peticiones HTTP/HTTPS y las reenvía a tu proceso Node en un puerto interno.
Ventajas típicas (sin configuración avanzada):
- HTTPS: termina TLS/SSL en el proxy y reenvía HTTP interno.
- Puertos estándar: el proxy escucha 80/443 y tu app puede escuchar 3000/8080 internamente.
- Balanceo: puede repartir tráfico entre varias instancias.
- Seguridad y límites: puede aplicar límites de tamaño, timeouts, etc.
Implicación práctica: tu app debe confiar en cabeceras como X-Forwarded-For o X-Forwarded-Proto si necesitas IP real o protocolo. En Express, suele activarse con:
app.set('trust proxy', 1);Úsalo solo si realmente estás detrás de un proxy; de lo contrario, podrías confiar en cabeceras falsificadas.
Checklist de despliegue (mínimo recomendado)
1) Logging útil en producción
- Registra inicio de servidor (puerto, entorno).
- Registra errores con stack (pero no lo devuelvas al cliente).
- Si puedes, usa logs estructurados (JSON) para facilitar búsqueda.
2) Manejo de errores y estabilidad del proceso
Aunque ya tengas middleware de errores, en producción conviene vigilar errores a nivel de proceso. Un enfoque básico es registrar y terminar el proceso para que el process manager lo reinicie (evita estados inconsistentes):
process.on('unhandledRejection', (reason) => {
console.error('Unhandled Rejection:', reason);
process.exit(1);
});
process.on('uncaughtException', (err) => {
console.error('Uncaught Exception:', err);
process.exit(1);
});Si usas PM2 u otro supervisor, el reinicio automático reduce el tiempo de caída.
3) Health check
Un endpoint de salud permite a un balanceador o a tu monitoreo saber si la API está viva. Debe ser rápido y sin dependencias pesadas.
app.get('/health', (req, res) => {
res.status(200).json({ status: 'ok' });
});Opcionalmente, puedes incluir checks superficiales (por ejemplo, conectividad a base de datos) en un endpoint separado como /ready para no degradar /health.
4) Verificación post-deploy con endpoints
Después de desplegar, valida desde fuera del servidor (tu máquina o una herramienta de CI) que:
GET /healthresponde 200.- Un endpoint funcional clave responde como se espera (por ejemplo,
GET /api/v1/items). - Los códigos de error se mantienen consistentes (por ejemplo, 404 para rutas inexistentes).
Ejemplo con curl:
curl -i https://tu-dominio.com/health
curl -i https://tu-dominio.com/api/v1/items
curl -i https://tu-dominio.com/ruta-que-no-existe5) Configuración de timeouts (concepto)
En producción, considera timeouts razonables para evitar conexiones colgadas. A nivel de Node/Express puedes ajustar timeouts del servidor HTTP, y a nivel de reverse proxy también existen. En un despliegue básico, al menos verifica que peticiones largas no bloqueen el proceso indefinidamente.