Adiós a process.env.UNDEFINED: variables de entorno con seguridad de tipos

3 min

1. La «trampa» de process.env

En una aplicación Node.js accedemos a las variables de entorno mediante process.env. Es un mecanismo sencillo y eficaz, pero tiene un problema inherente: no ofrece seguridad de tipos.

Los valores del objeto process.env son string o undefined. Esto provoca varios problemas habituales:

  • Un undefined inesperado: si olvidas definir una variable en el archivo .env o en el servidor, process.env.MY_VAR será undefined en tiempo de ejecución. Esto puede causar un TypeError en una parte profunda de la lógica.
  • Tipos que no coinciden: quizá esperes que un número de puerto sea de tipo number, pero process.env.PORT siempre es una cadena y tienes que llamar manualmente a parseInt en cada lugar donde lo utilizas.
  • Lógica de validación dispersa: solemos escribir código defensivo en distintos rincones, como const port = process.env.PORT || 3000;, lo que desordena la gestión de las variables de entorno.

2. Principio fundamental: fallar cuanto antes

Para una configuración crítica como las variables de entorno, una buena práctica es fallar cuanto antes (Fail-Fast).

Esto significa que la aplicación debe comprobar al comienzo del arranque si se han proporcionado todas las variables obligatorias y si su formato es correcto. Ante cualquier problema, debe lanzar inmediatamente un error y detenerse, en lugar de fallar más adelante, en un momento imprevisible, debido a una configuración incorrecta.

Así descubrimos los problemas de configuración de inmediato durante el despliegue o el desarrollo, y no cuando una persona accede a la aplicación.

3. Validación con seguridad de tipos mediante Zod

Zod es una biblioteca de declaración y validación de esquemas diseñada primero para TypeScript. Resulta especialmente adecuada para resolver el problema de seguridad de tipos de las variables de entorno.

Nuestra estrategia es:

  1. Definir un esquema Zod para todas las variables de entorno.
  2. Analizar process.env con ese esquema al arrancar la aplicación.
  3. Si el análisis falla —es decir, si no pasa la validación—, Zod lanza un error y el arranque de la aplicación falla.
  4. Si el análisis tiene éxito, obtenemos un objeto completamente tipado que utilizamos en toda la aplicación.

Ejemplo de implementación

Primero, instala Zod: pnpm add zod

Después, crea un archivo dedicado a las variables de entorno, por ejemplo src/env.ts:

// src/env.ts
import { z } from 'zod';

// 1. 定义 Schema
const envSchema = z.object({
  NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
  DATABASE_URL: z.string().min(1, 'DATABASE_URL is required.'),
  PORT: z.coerce.number().int().positive().default(3000),
  // z.coerce 会尝试将字符串转换为数字
});

// 2. 解析和导出
// .parse 会在验证失败时抛出错误,实现 "Fail-Fast"
export const env = envSchema.parse(process.env);

Ahora puedes importar desde src/env el objeto env en cualquier otra parte de la aplicación.

// src/server.ts
import { env } from './env'; // 导入经过验证和类型化的 env 对象

// env.PORT 的类型是 `number`,而不是 `string | undefined`
const port = env.PORT;

// env.NODE_ENV 的类型是 'development' | 'production' | 'test'
if (env.NODE_ENV === 'development') {
  console.log('Running in development mode');
}

// 如果 DATABASE_URL 未设置,应用在启动时就已经崩溃了,
// 所以在这里我们可以放心地认为它是存在的,并且类型是 `string`。
connectToDatabase(env.DATABASE_URL);

4. Ventajas

Este patrón aporta beneficios inmediatos:

  • Seguridad de tipos completa: cuando accedes a env.PORT en el código, TypeScript sabe que es un number.
  • Documentación y validación centralizadas: el propio archivo env.ts se convierte en la documentación oficial de las variables de entorno y reúne toda la lógica de validación.
  • Ejecución fiable: evita errores en tiempo de ejecución provocados por variables de entorno mal escritas, ausentes o con formato incorrecto.
  • Fallo temprano: cualquier problema de configuración se descubre inmediatamente durante el despliegue o el desarrollo.

Conclusión

Adelantar la validación de las variables de entorno al arranque de la aplicación y utilizar una herramienta como Zod para garantizar la seguridad de tipos es una práctica de ingeniería con una excelente relación entre esfuerzo y beneficio. Mejora notablemente la robustez de la aplicación y la confianza de quienes la desarrollan, y constituye una parte imprescindible de un proyecto TypeScript moderno.