En NestJS, validar correctamente los datos de entrada es clave para construir APIs seguras y mantenibles. En este artículo aprenderás qué son los decoradores de validación en NestJS, cómo funcionan junto a ValidationPipe y cómo usarlos en DTOs con ejemplos reales usando class-validator.
¿Qué son los decoradores de validación de NestJS?
En NestJS, los decoradores de validación son anotaciones que se aplican a las clases DTO (Data Transfer Objects) para definir reglas de validación de los datos de entrada (por ejemplo, el body de una petición HTTP).
Se basan principalmente en dos librerías:
- class-validator → define las reglas de validación
- class-transformer → transforma los datos entrantes a instancias de clases
NestJS las integra mediante el ValidationPipe.
¿Para qué sirven?
Permiten:
- Validar automáticamente datos de entrada (JSON, params, query)
- Evitar lógica de validación manual en controladores
- Centralizar y documentar reglas de negocio
- Mejorar seguridad y mantenibilidad
Flujo general de validación en NestJS
El cliente envía una petición (ej. POST con JSON)
- NestJS convierte el payload a un DTO
- Se ejecutan los decoradores de validación
- Si hay errores → NestJS responde con 400 Bad Request
- Si todo es válido → el controlador recibe el DTO tipado
Instalación necesaria
npm install class-validator class-transformer
Habilitar validaciones (ValidationPipe)
Global (recomendado)
// main.ts
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
}),
);
¿Qué hace cada opción?
whitelist: elimina propiedades no definidas en el DTOforbidNonWhitelisted: lanza error si llegan propiedades extratransform: convierte el payload a instancia del DTO
Ejemplo completo NestJS
En una clase Dto
import { IsString, IsInt, Min, IsOptional, IsEmail, Length } from 'class-validator';
export class CreateUserDto {
@IsString()
@Length(3, 50)
nombre: string;
@IsInt()
@Min(18)
edad: number;
@IsEmail()
email: string;
@IsOptional()
@IsString()
nickname?: string;
}
Uso en un controlador
@Post()
create(@Body() createUserDto: CreateUserDto) {
return createUserDto;
}
Decoradores de validación
Tipos básicos
| Decorador | Descripción | Ejemplo de uso | ✅ Válido | ❌ Inválido |
|---|---|---|---|---|
@IsString() |
Debe ser una cadena | name: "Juan" |
"Hola" |
123 |
@IsBoolean() |
Debe ser true o false |
activo: true |
true |
"true" |
@IsNumber() |
Debe ser numérico | precio: 99.5 |
42 |
"42" |
@IsInt() |
Entero solamente | edad: 25 |
25 |
25.5 |
@IsDecimal() |
Decimal válido | tasa: "12.50" |
"12.50" |
"abc" |
@IsArray() |
Arreglo válido | tags: ["a", "b"] |
["x", "y"] |
"no array" |
@IsObject() |
Objeto plano | data: {a: 1} |
{name: "x"} |
null |
@IsEnum(Rol) |
Dentro de un enum | Rol.ADMIN |
"ADMIN" |
"SUPERADMIN" |
@IsDate() |
Objeto Date |
new Date() |
new Date("2025-10-22") |
"2025-10-22" |
@IsDateString() |
Fecha en formato ISO | "2025-10-22T10:00:00Z" |
"2025-05-15T00:00:00Z" |
"15/05/2025" |
@IsUUID() |
UUID válido | "550e8400-e29b-41d4-a716-446655440000" |
"550e8400-e29b-41d4-a716-446655440000" |
"12345" |
Validaciones numéricas
| Decorador | Descripción | Ejemplo | ✅ Válido | ❌ Inválido |
|---|---|---|---|---|
@Min(10) |
Mínimo permitido | edad: 15 |
15 |
5 |
@Max(100) |
Máximo permitido | puntuacion: 80 |
80 |
120 |
@IsPositive() |
Mayor que 0 | monto: 5 |
5 |
-1 |
@IsNegative() |
Menor que 0 | saldo: -10 |
-10 |
5 |
@IsDivisibleBy(5) |
Múltiplo de 5 | valor: 10 |
10 |
12 |
Cadenas de texto
| Decorador | Descripción | Ejemplo | ✅ Válido | ❌ Inválido |
|---|---|---|---|---|
@Length(3, 10) |
Entre 3 y 10 caracteres | "Oscar" |
"Oscar" |
"O" |
@MinLength(5) |
Longitud mínima | "HolaMundo" |
"ABCDE" |
"Hi" |
@MaxLength(8) |
Longitud máxima | "Usuario" |
"Admin" |
"Administrador" |
@IsNotEmpty() |
No vacío | "valor" |
"texto" |
"" |
@IsEmpty() |
Debe estar vacío | "" |
"" |
"algo" |
@Matches(/^[A-Z]+$/) |
Cumple regex | "ABC" |
"HELLO" |
"Hello" |
@Contains("abc") |
Contiene substring | "zabcx" |
"123abc" |
"xyz" |
@NotContains("admin") |
No debe contener | "user123" |
"user123" |
"adminUser" |
@StartsWith("pre") |
Empieza con | "prefix" |
"preload" |
"loadpre" |
@EndsWith("end") |
Termina con | "theend" |
"weekend" |
"endless" |
@IsIn(["A", "B", "C"]) |
Dentro del conjunto | "B" |
"A" |
"Z" |
@IsNotIn(["X", "Y"]) |
No dentro del conjunto | "A" |
"A" |
"X" |
Formatos específicos
| Decorador | Descripción | Ejemplo | ✅ Válido | ❌ Inválido |
|---|---|---|---|---|
@IsEmail() |
Email válido | "a@b.com" |
"test@gmail.com" |
"correo@" |
@IsUrl() |
URL válida | "https://openai.com" |
"https://google.com" |
"www.google" |
@IsPhoneNumber('ES') |
Teléfono España | "+34611222333" |
"+34622333444" |
"611222333" |
@IsIP('4') |
IP v4 | "192.168.1.1" |
"10.0.0.1" |
"999.999.1.1" |
@IsPort() |
Puerto TCP | 8080 |
3000 |
70000 |
@IsFQDN() |
Dominio completo | "example.com" |
"google.com" |
"localhost" |
@IsBase64() |
Cadena Base64 | "U29tZVRleHQ=" |
"SGVsbG8=" |
"Hola" |
@IsHexColor() |
Color #hex |
"#AABBCC" |
"#ff0000" |
"red" |
@IsRgbColor() |
Color RGB válido | "rgb(255,0,0)" |
"rgb(0,128,255)" |
"rgba(0,0,0)" |
@IsLatitude() |
Latitud válida | 40.4168 |
-23.55 |
120.0 |
@IsLongitude() |
Longitud válida | -3.7038 |
-74.06 |
190.1 |
@IsTimeZone() |
Zona horaria | "Europe/Madrid" |
"America/Bogota" |
"Mars/Colony1" |
Validaciones de arreglos
| Decorador | Descripción | Ejemplo | ✅ Válido | ❌ Inválido |
|---|---|---|---|---|
@ArrayContains(['a']) |
Contiene los valores | ["a","b"] |
["a","x"] |
["x"] |
@ArrayNotContains(['z']) |
No contiene | ["a","b"] |
["x","y"] |
["a","z"] |
@ArrayMinSize(2) |
Mínimo tamaño | ["a","b"] |
["x","y"] |
["a"] |
@ArrayMaxSize(3) |
Máximo tamaño | ["a","b"] |
["1","2","3"] |
["1","2","3","4"] |
@ArrayUnique() |
Todos únicos | ["a","b"] |
["1","2","3"] |
["a","a"] |
Otros validadores útiles
| Decorador | Descripción | Ejemplo | ✅ Válido | ❌ Inválido |
|---|---|---|---|---|
@IsDefined() |
No puede ser undefined |
"valor" |
"dato" |
undefined |
@IsOptional() |
Se omite si falta | null o omitido |
undefined (ignorado) |
— |
@ValidateNested() |
Valida objeto hijo | user: UserDto |
{ user: {...} } |
{ user: "texto" } |
@Type(() => Clase) |
Transforma tipo (de class-transformer) |
@Type(() => User) |
✅ — | ❌ — |
@ValidateIf(o => o.edad > 18) |
Solo si condición cumple | — | Valida cuando edad > 18 |
Ignorado si no |