process.env es un objeto con una propiedad por cada variable de entorno que la shell pasó a tu programa de Node.js: process.env.PORT, process.env.DATABASE_URL, etc. Una variable que no se definió se lee como undefined, y una que sí se definió es siempre un string. Léela una vez al arrancar, dale un valor por defecto y conviértela al tipo que necesites.
Imprimió shop on port 8080 in eu-west (REGION nunca se definió, así que se usó su valor por defecto), y luego string number: la variable es texto hasta que Number() la convierte. Borra las dos primeras asignaciones y vuelve a ejecutar para ver todos los valores por defecto.
Definir variables de entorno en la shell
Las variables vienen de donde se lance Node.js. En una terminal:
// macOS and Linux: for one command
PORT=8080 NODE_ENV=production node app.js
// macOS and Linux: for the rest of the terminal session
export PORT=8080
// Windows PowerShell
$env:PORT = "8080"; node app.js
// Windows cmd.exe
set PORT=8080
node app.js
Las plataformas de hosting (Heroku, Render, Vercel, Docker, Kubernetes) tienen una página de configuración o un archivo para lo mismo; tu código las lee igual.
Todo es un string
De aquí salen la mayoría de los bugs con process.env. 'false' y '0' son strings no vacíos, así que son truthy:
La comprobación ingenua imprimió debug is ON para una variable con el valor false, así que compara con el string exacto que esperas. Con números, Number(process.env.X) convierte un valor que falta en NaN, así que aplica el valor por defecto antes de convertir, como hizo el primer ejemplo con ??.
NODE_ENV
NODE_ENV es una convención, no una funcionalidad de Node.js: Node.js nunca la define. La leen las librerías. Express, por ejemplo, oculta las stack traces en las páginas de error cuando vale production, y muchas herramientas se saltan comprobaciones solo de desarrollo.
Imprimió mode: production y verbose logs: false; cambia 'production' por 'development' y la segunda línea se invierte. Quédate con production, development y test, porque las librerías comprueban exactamente esas palabras.
Archivos .env: --env-file y dotenv
Escribir cada variable antes de node acaba cansando. Un archivo .env las guarda en la carpeta del proyecto, una por línea:
// .env
PORT=8080
DATABASE_URL=postgres://localhost/shop
API_KEY=dev-key-123
Node.js 20.6 y posteriores lo leen con un flag, sin paquetes:
node --env-file=.env app.js
En versiones anteriores, o si lo prefieres, instala el paquete dotenv (npm install dotenv) y cárgalo al principio de todo de tu archivo de entrada, antes de que ningún código lea process.env:
require('dotenv').config(); // copies .env into process.env
console.log(process.env.PORT); // 8080
Ninguno de los dos sobrescribe una variable que ya esté definida en la shell, así que la configuración de producción gana al archivo.
Falla pronto si faltan variables
Si falta la URL de la base de datos, el programa debería pararse al arrancar con un mensaje claro, no romperse con la primera petición una hora después:
Imprimió Missing environment variables: API_KEY; añade process.env.API_KEY = 'test'; al principio e imprime Config OK. En un programa real, sigue el mensaje con process.exit(1), porque un código de salida distinto de cero es lo que le indica a un script de despliegue o a una plataforma de contenedores que el arranque falló.
Nunca subas secretos al repositorio
Añade .env a .gitignore el mismo día que lo creas. Sube en su lugar un .env.example con los nombres de las variables y valores falsos, para que los demás sepan qué definir. Una clave que alguna vez se subió a un repositorio debe tratarse como filtrada y reemplazarse, aunque borres el commit después.
Preguntas frecuentes
¿Por qué process.env.MY_VAR es undefined?
La variable no se definió en la shell que lanzó Node.js, o se definió en otra terminal. Un archivo .env tampoco se lee automáticamente: lanza Node.js con --env-file=.env o cárgalo con dotenv antes de leer el valor.
¿Puedo cambiar process.env mientras el programa se ejecuta?
Sí. Asignar process.env.X = 'value' lo cambia para el proceso actual y para cualquier proceso hijo que lance después, pero nunca para la shell que lanzó Node.js. Node.js convierte a string el valor que asignas.
¿Los nombres de las variables de entorno distinguen mayúsculas?
En Linux y macOS, sí: PORT y port son dos variables. En Windows, no. Usa nombres en mayúsculas con guiones bajos y la duda nunca aparece.
¿Puede el navegador leer process.env?
No. Los navegadores no tienen variables de entorno. Herramientas de build como Vite y Next.js copian ciertas variables en tu código frontend al compilar, lo que también significa que esos valores son públicos.