Cómo funcionan los validadores
Cada validador es un decorador de propiedad que adjunta una regla de validación a los metadatos del campo. Cuando el esquema se ejecuta durante el arranque, las reglas se ejecutan en el orden en que se declararon en la propiedad. Cada regla puede incluir una fase de transform que convierte el string crudo al tipo objetivo y una fase de validate que comprueba si el valor convertido es aceptable. Si alguna regla devuelve un mensaje de error, el campo falla la validación.
Puedes apilar múltiples validadores en una sola propiedad. Un campo como una URI de Redis puede usar IsRedisUri para validar formato y Secret para enmascarado. El pipeline ejecuta cada regla secuencialmente, así que Transform realiza su conversión antes de que Validate vea el valor. Entender este orden es importante cuando combinas transformaciones personalizadas con validadores de tipo incorporados.
Los validadores se organizan en seis familias: validadores de string, número, booleano, red, Discord y datos. Un séptimo grupo, validadores comunes, contiene decoradores utilitarios que modifican el comportamiento del campo sin comprobar tipos.
Elegir bien los validadores
Comienza con el validador que coincida con el tipo en runtime que quieres en la aplicación, y luego agrega validadores de restricción para reglas de negocio más estrictas. En otras palabras, primero decide si el campo debe convertirse en string, number o boolean, y solo después agrega comprobaciones como longitud mínima, coincidencia de patrón o límites numéricos.
Ese orden hace que los esquemas sean más fáciles de leer. Cualquiera que abra el archivo de entorno puede entender rápidamente en qué se convierte el campo en el código y qué reglas adicionales debe cumplir.
Validadores de string
IsString confirma que el valor sea un string no vacío. Úsalo para cualquier campo de texto libre. Encima puedes apilar tres validadores de restricción para limitar aún más el valor.
| Decorator | Arguments | Description |
|---|---|---|
| IsString() | None | Valida que el valor sea un string no vacío. |
| MinLength(min) | min: number | Valida que el string tenga al menos el número de caracteres indicado. |
| MaxLength(max) | max: number | Valida que el string tenga como máximo el número de caracteres indicado. |
| Matches(pattern, message?) | pattern: RegExp, message?: string | Valida que el string coincida con la expresión regular dada. Acepta un mensaje de error personalizado opcional. |
@Env('APP_NAME')
@IsString()
@MinLength(3)
@MaxLength(64)
APP_NAME!: string;
@Env('SLUG', { secret: false })
@IsString()
@Matches(/^[a-z0-9-]+$/, 'Must be lowercase alphanumeric with hyphens')
SLUG!: string;Validadores numéricos
Tres validadores manejan valores numéricos. Cada uno transforma el string crudo en un número de JavaScript antes de validar su forma. Se diferencian en lo que aceptan después de la conversión.
| Decorator | Accepts | Example values |
|---|---|---|
| IsNumber | Cualquier número finito, incluidos decimales | "3.14", "42", "-1.5" |
| IsInteger | Solo números enteros | "42", "-7", "0" |
| IsPort | Entero entre 1 y 65535 | "3000", "8080", "443" |
Los tres transforman el string con Number() antes de validar. IsNumber rechaza NaN y strings vacíos. IsInteger además requiere que Number.isInteger() pase. IsPort agrega una comprobación de rango entre 1 y 65535 encima del requisito de entero.
Cuatro validadores de restricción funcionan sobre cualquier campo numérico y deben aplicarse después del validador de tipo:
| Decorator | Arguments | Description |
|---|---|---|
| Min(min) | min: number | Valida que el valor numérico sea mayor o igual que el mínimo indicado. |
| Max(max) | max: number | Valida que el valor numérico sea menor o igual que el máximo indicado. |
| IsPositive() | None | Valida que el valor numérico sea mayor que cero. |
| IsNegative() | None | Valida que el valor numérico sea menor que cero. |
@Env('RATE_LIMIT')
@IsNumber()
@Min(1)
@Max(1000)
RATE_LIMIT!: number;
@Env('MAX_RETRIES')
@IsInteger()
@IsPositive()
MAX_RETRIES!: number;
@Env('HTTP_PORT', { secret: false })
@IsPort()
HTTP_PORT!: number;Validador booleano
El validador IsBoolean transforma representaciones de string en booleanos nativos. Acepta cuatro valores crudos: "true", "false", "1" y "0". La comparación no distingue mayúsculas/minúsculas y recorta espacios, así que " TRUE " se resuelve como true. Cualquier otro valor falla la validación.
Después de la transformación, la propiedad contiene un boolean real de JavaScript. Nunca necesitas comparar contra el string "true" en tu código. Esta es la forma recomendada de manejar feature flags e interruptores almacenados en variables de entorno.
@Env('ENABLE_METRICS', { default: 'false', secret: false })
@IsBoolean()
ENABLE_METRICS!: boolean;Validadores de red
Cinco validadores cubren formatos comunes relacionados con red. Cada uno comprueba el string contra un patrón específico sin transformar el valor, porque las URI y direcciones de red permanecen como strings.
| Decorator | Validates | Example |
|---|---|---|
| IsUrl(options?) | Cualquier URL parseable con el constructor URL. Pasa la opción https para rechazar URL que no sean HTTPS. | "https://api.example.com" |
| IsEmail | Formato básico de email como [email protected] | "[email protected]" |
| IsHost | Hostname, dirección IPv4 o IPv6 | "localhost", "192.168.1.1" |
| IsMongoUri | URI que comienza con mongodb:// o mongodb+srv:// | "mongodb+srv://user:[email protected]/db" |
| IsRedisUri | URI que comienza con redis:// o rediss:// | "redis://localhost:6379" |
Los validadores de red funcionan bien con Secret para enmascarar credenciales en el log de inicio. Un string de conexión de MongoDB contiene una contraseña, por lo que el comportamiento de enmascarado por defecto la protege automáticamente. Los hostnames y puertos son seguros para mostrar, así que márcalos con secret: false en las opciones de @Env.
@Env('CALLBACK_URL', { secret: false })
@IsUrl({ https: true })
CALLBACK_URL!: string;
@Env('MONGO_URI')
@IsMongoUri()
MONGO_URI!: string;
@Env('REDIS_URI')
@IsRedisUri()
REDIS_URI!: string;
@Env('API_HOST', { default: 'localhost', secret: false })
@IsHost()
API_HOST!: string;Validadores de Discord
Cuatro validadores apuntan a formatos específicos de Discord. Son estrictos con la forma porque tokens, IDs o webhook URLs inválidos provocan fallos de autenticación difíciles de depurar en el momento de conectar.
| Decorator | Validates |
|---|---|
| IsDiscordToken() | String base64url de tres segmentos, 59-100 caracteres. Valida que cada segmento use solo caracteres base64url. |
| IsDiscordId() | Snowflake numérico, 17-19 dígitos. Rechaza caracteres no numéricos y longitudes fuera de rango. |
| IsDiscordWebhookUrl() | Webhook URL completa de Discord que coincide con la ruta de webhook en discord.com, canary.discord.com o ptb.discord.com. Marca siempre este campo con @Secret() porque la URL incrusta un token. |
| IsDiscordClientSecret() | OAuth2 client secret: string no vacío y seguro para URL de 24-64 caracteres sin espacios. Marca siempre este campo con @Secret(). |
Para valores específicos de Discord, la validación estricta vale la pena. Un webhook URL o client secret mal formado suele fallar lejos del archivo de entorno, por lo que la validación en el arranque es uno de los lugares más baratos para detectar el error.
@Env('DISCORD_TOKEN')
@IsDiscordToken()
TOKEN!: string;
@Env('GUILD_ID', { secret: false })
@IsDiscordId()
GUILD_ID!: string;
@Env('ALERT_WEBHOOK')
@IsDiscordWebhookUrl()
@Secret()
ALERT_WEBHOOK!: string;
@Env('DISCORD_CLIENT_SECRET')
@IsDiscordClientSecret()
@Secret()
CLIENT_SECRET!: string;Validadores de datos
Dos validadores manejan datos estructurados almacenados como strings en variables de entorno.
IsJson parsea el valor crudo con JSON.parse durante la fase de transformación. Si el parseo funciona, la propiedad contiene el objeto o array de JavaScript parseado. Si falla, el string crudo continúa y la fase de validación lo rechaza. Esto es útil para configuración compleja que quieres guardar como string JSON en una única variable de entorno.
IsEnum acepta un array de valores permitidos y comprueba que el valor resuelto coincida con uno de ellos. No transforma el valor, así que funciona con strings por defecto. Puedes combinarlo con un validador de tipo como IsNumber si los valores de tu enum son numéricos, ya que IsEnum ejecuta su comprobación después de que todas las transformaciones terminan.
@Env('FEATURE_FLAGS')
@IsJson()
FEATURE_FLAGS!: Record<string, boolean>;
@Env('LOG_LEVEL', { default: 'info', secret: false })
@IsEnum(['debug', 'info', 'warn', 'error'])
LOG_LEVEL!: string;Validadores comunes
Los validadores comunes no comprueban tipos. En su lugar, modifican cómo se comporta el campo en el pipeline. Se usan junto con validadores de tipo para controlar opcionalidad, enmascarado, valores por defecto y lógica personalizada.
IsOptional y Default hacen que el campo sea opcional, pero Default además proporciona un fallback concreto. Si usas Default, el campo siempre se resuelve a un valor. Si usas solo IsOptional, la propiedad puede ser undefined en runtime.
Transform intercepta el string crudo y devuelve el nuevo valor. Se ejecuta antes de cualquier paso de validate en la misma regla y antes de las reglas declaradas debajo. Esto permite normalizar datos, como recortar espacios o separar listas por comas, antes de que el validador de tipo vea el valor.
Validate agrega una comprobación personalizada al pipeline. La función recibe el valor ya transformado y devuelve true cuando es aceptable o un mensaje de error string cuando no lo es. Esta es la vía de escape para reglas de negocio que los validadores incorporados no cubren.
@Env('ALLOWED_ORIGINS', { secret: false })
@Transform((raw) => raw.split(',').map((s) => s.trim()))
@Validate((value) => Array.isArray(value) && value.length > 0 ? true : 'Expected at least one origin')
ALLOWED_ORIGINS!: string[];