Validación y pipes en NestJS


En este post, exploraremos uno de los conceptos más esenciales y poderosos de NestJS: Validación y Pipes.

oscar Escrito por oscar 03 January 2025 102 0

En este post, exploraremos uno de los conceptos más esenciales y poderosos de NestJS: Validación y Pipes. Estos elementos permiten mantener la integridad de los datos y mejorar la seguridad de nuestras aplicaciones.

¿Qué son las Pipes en NestJS?

En NestJS, las Pipes son clases que se utilizan para transformar datos de entrada o validarlos antes de que lleguen a los controladores. Este concepto es esencial para mantener la consistencia en el flujo de datos dentro de tu aplicación.

Algunas características clave de las Pipes son:

  • Transformación de datos: Convierte el dato de entrada al formato deseado.

  • Validación de datos: Verifica que los datos cumplan con ciertas reglas.

  • Se pueden aplicar a nivel global, controlador, o método.

Uso Básico de Pipes

NestJS incluye algunas Pipes integradas, como ParseIntPipe. Aquí tienes un ejemplo:

@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
  return `El ID es: ${id}`;
}

En este caso, ParseIntPipe asegura que el parámetro id sea convertido a un entero.

Uso de DTOs (Data Transfer Objects).

En NestJS, los DTOs (Data Transfer Objects) se usan para definir la estructura de datos que se enviará y recibirá en las solicitudes HTTP. Los DTOs ayudan a asegurar que los datos cumplan con ciertas reglas antes de procesarlos en los controladores y servicios, y suelen implementarse como clases de TypeScript.

Crear un DTO Básico

El siguiete ejemplo se crea una clase DTO basica para un usuario

// src/users/dto/create-user.dto.ts
export class CreateUserDto {
  username: string;
  email: string;
  password: string;
}

Para usarla en el controlador

// src/users/users.controller.ts
import { Controller, Post, Body } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';

@Controller('users')
export class UsersController {

  @Post()
  async createUser(@Body() createUserDto: CreateUserDto) {
    return `Usuario creado: ${JSON.stringify(createUserDto)}`;
  }
}

Validación de datos con class-validator y class-transformer

Un DTO en NestJS generalmente se define como una clase, utilizando decoradores de class-validator y class-transformer para validar y transformar datos.

Ejemplo de DTO: Imaginemos que tienes una entidad User con propiedades como username, email, y password. Puedes crear un DTO llamado CreateUserDto que defina las propiedades y reglas de validación para los datos de entrada al crear un usuario.

Instala las dependencias necesarias:

npm install class-validator class-transformer

Luego, crea el archivo del DTO:

// src/users/dto/create-user.dto.ts
import { IsString, IsEmail, IsNotEmpty, MinLength } from 'class-validator';

export class CreateUserDto {
  @IsString()
  @IsNotEmpty()
  username: string;

  @IsEmail()
  email: string;

  @IsString()
  @MinLength(6)
  password: string;
}

En este ejemplo:

  • @IsString() valida que el valor sea una cadena.
  • @IsNotEmpty() asegura que el campo no esté vacío.
  • @IsEmail() valida que el campo contenga una dirección de correo electrónico válida.
  • @MinLength(6) especifica que password debe tener al menos 6 caracteres.

Para aplicar el DTO en un controlador, se usa en el parámetro de un método de controlador junto con el decorador @Body(). Esto permite que NestJS valide automáticamente los datos de la solicitud.

// src/users/users.controller.ts
import { Controller, Post, Body } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UsersService } from './users.service';

@Controller('users')
@UsePipes(new ValidationPipe())
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Post()
  async createUser(@Body() createUserDto: CreateUserDto) {
    return this.usersService.createUser(createUserDto);
  }
}

Activar la validación global en la aplicación

Para que la validación funcione correctamente en toda la aplicación, activa la validación global en el archivo principal main.ts:

// src/main.ts
import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // Activar la validación global
  app.useGlobalPipes(new ValidationPipe({
    whitelist: true,
  }));
  
  await app.listen(3000);
}
bootstrap();

En este caso, @Body() indica que el CreateUserDto se llenará con el contenido del cuerpo de la solicitud y validará los datos automáticamente. Si los datos no cumplen con las reglas definidas en el DTO, NestJS lanzará un error de validación.

Pipes: Transformación y validación de datos en rutas

En NestJS, los Pipes son funciones que se ejecutan antes de que el controlador procese una solicitud. Su función principal es transformar y validar los datos de entrada, asegurando que los controladores solo reciban datos que cumplen con los requisitos especificados. Los pipes permiten, por ejemplo, verificar que los datos enviados por el cliente tengan el formato correcto o convertir valores de texto a números cuando se necesite.

Funcionalidad de los Pipes

  • Transformación: Modifican los datos de la solicitud antes de que lleguen al controlador. Por ejemplo, un pipe puede convertir un valor de tipo string en un entero.
  • Validación: Verifican que los datos cumplen ciertos criterios y lanzan una excepción en caso de que no se cumplan. Si los datos son inválidos, el pipe impide que la solicitud llegue al controlador y retorna un error.

Tipos de Pipes en NestJS

NestJS incluye algunos pipes ya definidos, y también permite crear pipes personalizados según las necesidades de la aplicación.

Ejemplos de Pipes Integrados en NestJS

  • ParseIntPipe: Convierte un valor string a entero. Útil cuando los parámetros de URL deben ser enteros.
  • ValidationPipe: Valida los datos de entrada usando decorators como @IsString(), @IsInt(), entre otros, que se definen en los DTOs.

Ejemplo

Este pipe convierte el parámetro id a un número entero. Si el valor no es un número, lanzará una excepción automáticamente.

import { Controller, Get, Param, ParseIntPipe } from '@nestjs/common';

@Controller('users')
export class UsersController {
  @Get(':id')
  getUserById(@Param('id', ParseIntPipe) id: number) {
    return { message: `Usuario con ID ${id}` };
  }
}

Uso de ValidationPipe con DTOs

El ValidationPipe se usa junto con los Data Transfer Objects (DTOs) para validar que los datos recibidos cumplen con ciertas reglas. En este caso, se requiere que el name sea una cadena y que age sea un entero positivo.

Define un DTO para validar los datos:

// src/users/dto/create-user.dto.ts
import { IsString, IsInt, Min } from 'class-validator';

export class CreateUserDto {
  @IsString()
  name: string;

  @IsInt()
  @Min(1)
  age: number;
}

Configura el controlador para usar ValidationPipe:

import { Controller, Post, Body, UsePipes, ValidationPipe } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';

@Controller('users')
export class UsersController {
  @Post()
  @UsePipes(new ValidationPipe())
  createUser(@Body() createUserDto: CreateUserDto) {
    return {
      message: 'Usuario creado',
      user: createUserDto,
    };
  }
}

En este ejemplo:

ValidationPipe valida automáticamente los datos del Body usando las reglas definidas en el DTO. Si algún campo no cumple con los requisitos (name no es un string o age no es un número positivo), NestJS devolverá un error de validación sin llegar al controlador.

Creación de un pipe personalizado

Si necesitas una transformación o validación específica, puedes crear tu propio pipe:

import { PipeTransform, Injectable, BadRequestException } from '@nestjs/common';

@Injectable()
export class UppercasePipe implements PipeTransform {
  transform(value: any) {
    if (typeof value !== 'string') {
      throw new BadRequestException('El valor debe ser un string');
    }
    return value.toUpperCase(); // Convierte el valor a mayúsculas
  }
}

Para utilizarlo:

import { Controller, Post, Body } from '@nestjs/common';
import { UppercasePipe } from './pipes/uppercase.pipe';

@Controller('items')
export class ItemsController {
  @Post()
  createItem(@Body('name', UppercasePipe) name: string) {
    return { name };
  }
}

 

 


Comentario

Debe aceptar antes de enviar