Despliegue básico de una API Node.js con Express

Capítulo 12

Tiempo estimado de lectura: 7 minutos

+ Ejercicio

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.

Continúa en nuestra aplicación.
  • Escuche el audio con la pantalla apagada.
  • Obtenga un certificado al finalizar.
  • ¡Más de 5000 cursos para que explores!
O continúa leyendo más abajo...
Download App

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

AspectoDevProd
ArranqueCon recarga (nodemon)Node directo o process manager
LogsMuy verbososEstructurados, nivel controlado
ErroresDetalles para depurarMensajes seguros, sin filtrar internals
Configuración.env localVariables del servidor/servicio
Escalado1 proceso1+ 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 PORT y ejecutas npm 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=production
  • PORT=8080
  • DATABASE_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 start

Limitació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-api

Para 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-api

PM2 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 /health responde 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-existe

5) 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.

Ahora responde el ejercicio sobre el contenido:

¿Cuál es una práctica recomendada para evitar fallos al desplegar una API Express cuando el servidor asigna el puerto dinámicamente?

¡Tienes razón! Felicitaciones, ahora pasa a la página siguiente.

¡Tú error! Inténtalo de nuevo.

En producción el entorno suele definir el puerto. Si la app lo hardcodea, puede no coincidir (por ejemplo, PORT=8080) y el despliegue fallará. Por eso se recomienda usar process.env.PORT con un fallback local.

Portada de libro electrónico gratuitaNode.js para principiantes: crea un backend simple con Express
100%

Node.js para principiantes: crea un backend simple con Express

Nuevo curso

12 páginas

Descarga la aplicación para obtener una certificación gratuita y escuchar cursos en segundo plano, incluso con la pantalla apagada.