En esta página
Pipes y validación de datos
¿Qué son los pipes en NestJS?
Los pipes son clases que implementan la interfaz PipeTransform y se ejecutan antes de que el método del controlador reciba los datos. Tienen dos propósitos principales:
- Transformación: Convertir los datos de entrada a la forma deseada (string a número, string a fecha, plainObject a instancia de clase, etc.)
- Validación: Evaluar los datos de entrada y lanzar una excepción si no son válidos
Los pipes son una capa defensiva que garantiza que tu lógica de negocio nunca recibe datos malformados o inesperados.
Pipes integrados de NestJS
NestJS incluye varios pipes listos para usar en el paquete @nestjs/common:
import {
ValidationPipe, // valida con class-validator
ParseIntPipe, // convierte string a número entero
ParseFloatPipe, // convierte string a número decimal
ParseBoolPipe, // convierte 'true'/'false' a boolean
ParseUUIDPipe, // valida formato UUID
ParseEnumPipe, // valida que el valor esté en un enum
ParseArrayPipe, // convierte strings separados por coma a array
DefaultValuePipe, // proporciona un valor por defecto si es null/undefined
} from '@nestjs/common';Ejemplos de uso
@Controller('articulos')
export class ArticulosController {
// ParseIntPipe — lanza BadRequestException si no es un número válido
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
return this.service.findOne(id);
}
// ParseUUIDPipe — valida el formato UUID
@Get('uuid/:uuid')
findByUuid(@Param('uuid', ParseUUIDPipe) uuid: string) {
return this.service.findByUuid(uuid);
}
// DefaultValuePipe + ParseIntPipe combinados
@Get()
findAll(
@Query('pagina', new DefaultValuePipe(1), ParseIntPipe) pagina: number,
@Query('limite', new DefaultValuePipe(10), ParseIntPipe) limite: number,
) {
return this.service.findAll({ pagina, limite });
}
// ParseEnumPipe — valida que el valor esté en el enum
@Get('estado/:estado')
findByEstado(
@Param('estado', new ParseEnumPipe(EstadoArticulo)) estado: EstadoArticulo,
) {
return this.service.findByEstado(estado);
}
}class-validator — La librería de validación
class-validator es la librería estándar de validación para NestJS. Usa decoradores en las clases DTO para declarar las reglas de validación:
npm install class-validator class-transformerLos decoradores más utilizados son:
// Strings
@IsString() // es string
@IsNotEmpty() // no está vacío (ni '')
@MinLength(3) // longitud mínima
@MaxLength(100) // longitud máxima
@Matches(/regex/) // debe coincidir con la expresión regular
// Números
@IsNumber() // es número
@IsInt() // es entero
@Min(0) // valor mínimo
@Max(100) // valor máximo
@IsPositive() // mayor que 0
// Colecciones
@IsArray() // es array
@ArrayMinSize(1) // tamaño mínimo del array
@ArrayMaxSize(10) // tamaño máximo
@IsIn(['a', 'b']) // el valor está en la lista
// Booleanos y enums
@IsBoolean() // es booleano
@IsEnum(MiEnum) // el valor está en el enum
// Formato especial
@IsEmail() // formato de email
@IsUrl() // URL válida
@IsUUID() // UUID válido
@IsISO8601() // fecha ISO 8601 válida
// Opcionalidad
@IsOptional() // el campo puede estar ausente o ser null/undefinedclass-transformer — Transformación de datos
class-transformer complementa a class-validator con decoradores de transformación:
import { Transform, Type, Exclude, Expose } from 'class-transformer';
export class UsuarioDto {
@Transform(({ value }: { value: string }) => value.trim().toLowerCase())
email: string;
@Type(() => Number) // convierte string a número durante la deserialización
edad: number;
@Exclude() // excluye este campo de la serialización
password: string;
}El decorador @Exclude() es especialmente útil para evitar que campos sensibles (contraseñas, tokens) aparezcan en las respuestas JSON.
ValidationPipe — Configuración avanzada
El ValidationPipe es el pipe que conecta class-validator y class-transformer con NestJS:
new ValidationPipe({
whitelist: true, // elimina propiedades no declaradas en el DTO
forbidNonWhitelisted: true, // lanza error 400 si hay propiedades extra
transform: true, // transforma el payload al tipo del DTO
disableErrorMessages: false, // en producción, podrías ocultar mensajes detallados
validationError: {
target: false, // no incluye el objeto validado en el error
value: false, // no incluye el valor inválido en el error
},
exceptionFactory: (errors) => {
// Personaliza el formato de los errores de validación
const mensajes = errors.map(err =>
Object.values(err.constraints ?? {}).join(', ')
);
return new BadRequestException({
statusCode: 400,
mensaje: 'Error de validación',
errores: mensajes,
});
},
})Validación de arrays anidados
Para validar objetos anidados y arrays de objetos, usa @ValidateNested y @Type:
import { ValidateNested, IsArray } from 'class-validator';
import { Type } from 'class-transformer';
export class DireccionDto {
@IsString()
calle: string;
@IsString()
ciudad: string;
}
export class CrearPedidoDto {
@ValidateNested()
@Type(() => DireccionDto)
direccion: DireccionDto;
@IsArray()
@ValidateNested({ each: true })
@Type(() => ItemPedidoDto)
items: ItemPedidoDto[];
}Crear un pipe personalizado
Cuando los pipes integrados no son suficientes, puedes crear el tuyo propio implementando PipeTransform:
// pipes/slugify.pipe.ts
import { PipeTransform, Injectable } from '@nestjs/common';
@Injectable()
export class SlugifyPipe implements PipeTransform<string, string> {
transform(value: string): string {
return value
.toLowerCase()
.normalize('NFD') // descompone caracteres acentuados
.replace(/[\u0300-\u036f]/g, '') // elimina los diacríticos
.replace(/[^a-z0-9\s-]/g, '') // elimina caracteres especiales
.trim()
.replace(/[\s_-]+/g, '-') // convierte espacios a guiones
.replace(/^-+|-+$/g, ''); // elimina guiones al inicio/final
}
}
// Uso:
@Post()
create(@Body('titulo', SlugifyPipe) slug: string) {
return { slug }; // 'Hola Mundo' → 'hola-mundo'
}Alcance de los pipes
Al igual que los guards e interceptores, los pipes se pueden aplicar en varios niveles:
// Nivel de parámetro (más granular)
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {}
// Nivel de método
@Post()
@UsePipes(new ValidationPipe())
create(@Body() dto: CrearArticuloDto) {}
// Nivel de controlador
@Controller('productos')
@UsePipes(ValidationPipe)
export class ProductosController {}
// Nivel global (en main.ts) — aplicado a todos los controladores
app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true }));La validación sólida de datos de entrada es una de las primeras líneas de defensa de cualquier API. En la siguiente lección aprenderemos a implementar la segunda línea de defensa: los guards de autorización.
Inicia sesión para guardar tu progreso