No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Validando parámetros con class-validator y mapped-types

20/23
Recursos

Los DTO no solo sirven para tipar y determinar la estructura de los datos de entrada de un endpoint, también pueden contribuir en la validación de los datos y en la entrega de mensajes al front-end en caso de error en los mismos.

Validación de datos con DTO

Utiliza el comando npm i class-validator class-transformer para instalar dos dependencias que nos ayudarán en la validación de los datos.
Estas librerías traen un set de decoradores para las propiedades de los DTO y así validar los tipos de datos de entrada.

import { IsNotEmpty, IsString, IsNumber, IsUrl } from 'class-validator';

export class CreateProductDTO {
  @IsNotEmpty()
  @IsString()
  readonly name: string;
  
  @IsNotEmpty()
  @IsString()
  readonly description: string;
  
  @IsNotEmpty()
  @IsNumber()
  readonly price: number;
  
  @IsNotEmpty()
  @IsUrl()
  readonly image: string;
}

Estas validaciones contribuyen en la experiencia de desarrollo devolviendo mensajes al front-end sobre qué datos están faltando o cuáles no son correctos. Por ejemplo, si en el Body de la petición enviamos.

{
  "name": "Nombre producto",
  "price": 100,
  "image": "imagen"
}

El servidor nos devolverá el siguiente mensaje:

{
  "statusCode": 400,
  "message": [
    "description should not be empty",
    "description must be a string",
    "image must be an URL address"
  ],
  "error": "Bad Request"
}

Indicando que la solicitud espera de forma obligatoria un campo description del tipo string y un campo image con una URL.

Cómo reutilizar código de los DTO

A medida que tu aplicación crezca, tendrás que crear muchos DTO, para la creación de un producto, edición, filtros, etc. Una buena práctica es la reutilización de las clases DTO que ya tengas implementado para no repetir propiedades.

Instala la dependencia @nestjs/mapped-types que nos ayudará con la reutilización de código de la siguiente manera.

import { IsNotEmpty, IsString, IsNumber, IsUrl } from 'class-validator';
import { PartialType, OmitType } from '@nestjs/mapped-types';

export class CreateProductDTO {
  
  @IsNotEmpty()
  @IsString()
  readonly name: string;
  
  @IsNotEmpty()
  @IsString()
  readonly description: string;
  
  @IsNotEmpty()
  @IsNumber()
  readonly price: number;
  
  @IsNotEmpty()
  @IsUrl()
  readonly image: string;
}

export class UpdateProductDto extends PartialType(
  OmitType(CreateProductDTO, ['name']),
) {}

Importa PartialType y OmitType desde @nestjs/mapped-types.

PartialType permite extender una clase de otra y que todos sus campos sean opcionales. Así, el DTO UpdateProductDto no tiene como obligatorio sus campos y es posible editar todos o solo uno.

Por otro lado, OmitType, permite la omisión de campos haciendo que cierta cantidad de ellos no formen parte del DTO en el caso de que dichos campos no deban ser editados.

Instalar npm i class-validator class-transformer @nestjs/mapped-types

// src/dtos/products.dtos.ts
import {
  IsString,
  IsNumber,
  IsUrl,
  IsNotEmpty,
  IsPositive,
} from 'class-validator';
import { PartialType } from '@nestjs/mapped-types';

export class CreateProductDto {
  @IsString()
  @IsNotEmpty()
  readonly name: string;

  @IsString()
  @IsNotEmpty()
  readonly description: string;

  @IsNumber()
  @IsNotEmpty()
  @IsPositive()
  readonly price: number;

  @IsNumber()
  @IsNotEmpty()
  @IsPositive()
  readonly stock: number;

  @IsUrl()
  @IsNotEmpty()
  readonly image: string;
}

export class UpdateProductDto extends PartialType(CreateProductDto) {}
// src/main.ts
import { ValidationPipe } from '@nestjs/common';

async function bootstrap() {
  ...
  app.useGlobalPipes(new ValidationPipe());
  ...
}
bootstrap();

Contribución creada por: Kevin Fiorentino.

Aportes 39

Preguntas 24

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Con class-validator también tenemos la opción de retornar mensajes personalizados para cada una de las validaciones.
Solo basta con, en la validación que corresponda, agregar un objeto con la key message que contendra dicho mensaje.

Y la respuesta de la API se verá algo así:

npm i class-validator class-transformer @nestjs/mapped-types

Tremendo aporte de los validadores. Es increíble todo lo que nos brinda Nest.js.

Si quieren agregar un campo en el DTO con tipo Date pueden hacer esto:

  1. Permiten la transformacion en el main.ts
  app.useGlobalPipes(new ValidationPipe({ transform: true }));
  1. Agregan @Type de la libreria class-transform
@IsDate()
@Type(() => Date)
@IsNotEmpty()
readonly date: Date;
  1. En el request pueden enviar varios formatos, por ej:
    "date": "06/18/2021",

Validar que los tipos coincidan con los DTOs

Instalar dependencias: npm install @nestjs/mapped-types class-validator class-transformer

Importar las dependencias en el main.ts

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

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe());
  await app.listen(3000);
}
bootstrap();

importar en el DTO:

import {
  IsString,
  IsNumber,
  IsUrl,
  IsNotEmpty,
  IsPositive,
} from 'class-validator';

import { PartialType } from '@nestjs/mapped-types';

Implementar en la clase del DTO:

export class CreateProductDto {
  @IsString()
  @IsNotEmpty()
  readonly name: string;

  @IsString()
  @IsNotEmpty()
  readonly description: string;

  @IsNumber()
  @IsNotEmpty()
  @IsPositive()
  readonly price: number;

  @IsNumber()
  @IsNotEmpty()
  @IsPositive()
  readonly stock: number;

  @IsUrl() //valida que sea una url
  @IsNotEmpty()
  readonly image: string;
}

Se puede extender a una clase pero que sea opcional pero que valida sus campos de esta forma:

export class UpdateProductDto extends PartialType(CreateProductDto) {}

Se puede extender un mensaje custom al decorador

@IsString({message: 'My custom message'})

Wow no conocía estas opciones de nest, y al principio parece too much, para una api bien robusta me parece perfecto.

Al querer instalar @nestjs/mapped-types me encontre con problemas de dependencias internas con class-transformer.
En este momento (noviembre 2021) class-transformer esta en version 0.5.1 y @nestjs/mapped-types requiere 0.4.0.
Solucion instalar: npm i [email protected] y luego npm i @nestjs/mapped-types

Ok estas validaciones son middleware, creería yo…

Al día de hoy a mi no me funciono el comando

npm i @nestjs/mapped-types

Ya que según la documentación de nest ahora dice que debemos usar @nestjs/swagger

npm i @nestjs/swagger

y para que realmente sea opcional debemos agregar el decorador @ApiProperty() para que PartialType los pueda omitir quedando de la siguiente manera

export class CreateProductDto {
  @IsNotEmpty()
  @IsString()
  @ApiProperty()
  readonly name: string;
  @IsNotEmpty()
  @IsString()
  @ApiProperty()
  readonly description: string;
  @IsNotEmpty()
  @IsNumber()
  @IsPositive()
  @ApiProperty()
  readonly stock: number;
  @IsNotEmpty()
  @IsUrl()
  @ApiProperty()
  readonly image: string;
  @IsNotEmpty()
  @IsNumber()
  @IsPositive()
  @ApiProperty()
  readonly price: number;
}

Fuente: https://docs.nestjs.com/openapi/mapped-types#mapped-types

Para quienes estén haciendo el curso y tengan un error al instalar mapped-types deben usar la versión 0.13.0 de class-validator, hasta ahora no es compatible con la versión 0.14.0 que es la que se instala por defecto

npm i [email protected]

nest es BRUTAL

uno podria marcar a una clase con un me gusta por ejemplo esta, esta buenisima la validacion de Typos como lo hace NestJS

Importante

Recuerden poner el nombre de las importaciones de class-validator en CammelCase, o sea, con la primera letra en mayuscula, ya que tambien tienen su opcion de ponerlo en minuscula pero en esa le tienes que pasar el valor dentro de los parentesis.

uff que hit este tema

Normalmente cuando hago apis las hago con express + typescript, y este tipo de validaciones las hago con express-validators, y es un poco engorroso crear middlewares para responder el error. Crear dtos es mucho más sencillo.

Se puede implementar el uso de utility types de typescript para los dtos.
Para el caso del método PUT se podría usar Partial, sin tener que generar una clase nueva únicamente para ese método.

export class ProductDto {
  @IsString()
  @IsNotEmpty()
  readonly name: string;

  @IsString()
  @IsNotEmpty()
  readonly description: string;

  @IsNumber()
  @IsNotEmpty()
  readonly price: number;

  @IsNumber()
  @IsNotEmpty()
  readonly stock: number;

  @IsUrl()
  @IsNotEmpty()
  readonly image: string;
}

  • Controlador @Put:
  @Put(':id')
  update(
      @Param('id', ParseIntPipe) id: number, 
      @Body()  payload: Partial<ProductDto>) {
      return this._productService.update(id, payload); 
   }
  • Service
  update(id: number, payload: Partial<ProductDto>) {}

Hola 👋
Para los que preferimos yarn sobre npm

yarn add class-validator class-transformer

Espero haberles aportado algo 🚀

Instalar una dependencia de @nestjs mapped-types nos ayuda a reutilizar código de clases que ya tenemos

import { ValidationPipe } from '@nestjs/common';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe());
  await app.listen(3000);
}
Si quisieran crear productos sin descripción, deben de usar la configuración `skipUndefinedProperties` ```js async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalPipes(new ValidationPipe({ skipUndefinedProperties: true })); app.enableCors(); await app.listen(3000); } bootstrap(); ```El problema es que ahora se puede omitir cualquier propiedad como price o stock. Para solucionarlo de sebe usar `@IsDefined()` ```js import { PartialType } from '@nestjs/mapped-types'; import { IsDefined, IsNumber, IsPositive, IsString, IsUrl } from 'class-validator'; export class CreateProductDto { @IsDefined() @IsString() readonly name: string; @IsString() readonly description?: string; @IsDefined() @IsNumber() @IsPositive() readonly price: number; @IsDefined() @IsNumber() readonly stock: number; @IsDefined() @IsUrl() readonly image: string; } export class UpdateProductDto extends PartialType(CreateProductDto) {} ```

Para 2024 se usa el paquete @nestjs/swagger

npm i class-validator class-transformer @nestjs/swagger

Y este seria el codigo de la clase actualizado:

import { ApiProperty, PartialType } from '@nestjs/swagger';
import {
  IsNotEmpty,
  IsNumber,
  IsPositive,
  IsString,
  IsUrl,
} from 'class-validator';

export class CreateProductDto {
  @ApiProperty()
  @IsNotEmpty()
  @IsString()
  readonly name: string;

  @ApiProperty()
  @IsNotEmpty()
  @IsString()
  readonly description: string;

  @ApiProperty()
  @IsNotEmpty()
  @IsNumber()
  @IsPositive()
  readonly price: number;

  @ApiProperty()
  @IsNotEmpty()
  @IsNumber()
  @IsPositive()
  readonly stock: number;

  @ApiProperty()
  @IsNotEmpty()
  @IsUrl()
  readonly image: string;
}

export class UpdateProductDto extends PartialType(CreateProductDto) {}

Ahora bien, si quieren usar otra format de validación como con Zod les puede ayudar esta guía: https://medium.com/@juliojordan/nestjs-and-zod-eff1e3892c05, enlace

Sinceramente no creo que sea la mejor opción utilizar decoradores para validar los objetos que vienen de la petición. He encontrado una forma mucho mejor de hacerlo utilizando los esquemas de la librería Joi y los pipes de Nest: <https://duprez.medium.com/validaci%C3%B3n-con-joi-y-nestjs-f11cc32dbf4b>
Si alguien hizo como yo y creo algunas interfaces en vez de clases, TypeScript no permite usar decoradores en las interfaces. Así que si quieren usar el paquete class-validator tiene que ser dentro de una clase. 😐

Me encontré la documentación de Nest sobre este tema acá

Que maravilloso.

el cero no lo válida como positivo

Es increible la cantidad de tiempo que nos ahorran los decoradores de validación. Aunque solo es un layer más ya que la robustez de los datos debe siempre estar en los modelos. Excelente clase!

Add @nestjs/mapped-types

Using Class validator annotations for execution time validation not only dev experience

Add class-validator and class-transformer

Bonito aporte!

Al venir de express, me faltó gritar de alegría al ver estos decoradores!!! es increible la cantidad de trabajo que nos ahorran.

Utilizar decorador @isPositive para numeros positivos de class-validator

Activar las validaciones de class-validator

class-validator tienes muchos decoradores uno de ellos son: @isString, @isNumber,@isUrl, @isEmail, @isNotEmpity

instalar la dependencia class-validator y la class-transformer

Encantado con esta parte de la validación, sobre todo el uso de Dtos, que es de gran apoyo.

PartialType

👏👏