DEV Community

Tamim Ikbal
Tamim Ikbal

Posted on

Setup Validation Pipes & Yup for NestJS Request Validation.

Hello my friends, Hope you are all doing well. Today we will set up a Validation Pipes to validate Requests based on the Yup Validation rule schema. Let’s Code.

1. Creating our NestJS Project

npm i -g @nestjs/cli
nest new project-name
Enter fullscreen mode Exit fullscreen mode

2. Install YUP

Yup is a powerful schema builder for runtime value parsing and validation. So in this article, we will use Yup for validation.

npm i yup
yarn add yup
Enter fullscreen mode Exit fullscreen mode

3. Create The Todo DTO And Validation Schema

Let's say we have an application where we require a Title and Description.

import { object, string, InferType } from 'yup';

export const createTodoSchema = object({
  title: string().min(3).required(),
  description: string().required(),
});

export type createTodoDto = InferType<typeof createTodoSchema>;
Enter fullscreen mode Exit fullscreen mode

Here we created a Yup object with a set of validation rules and export then we generated and export the todo DTO createTodoDto from createTodoSchema;

4. Create A Validator Pipes

Now, We have to create a Validation Pipes with will validate the Request based on our giver YUP Object.

validator.pipes.ts

import {
    ArgumentMetadata,
    HttpException,
    HttpStatus,
    Injectable,
    PipeTransform,
  } from '@nestjs/common';
  import { Schema, ValidationError } from 'yup';

  @Injectable()
  export class Validator implements PipeTransform {
    constructor(private schema: Schema) {}

    transform(value: any, metadata: ArgumentMetadata) {
      try {
        //Validate the request input data based on the Validation rule..
        return this.schema.validateSync(value, { abortEarly: false });
      } catch (error) {

        //When got error from YUP catch it and process the errors.
        if (error instanceof ValidationError) {
          const errors = {};

          //Formate the errors
          error.inner.forEach((err: ValidationError) => {
            const path = err.path || '';
            const messages = errors[path] || [];
            errors[path] = [err.message, ...messages];
          });

          //Throw the Exceptoom with The errors and Status code 422.
          throw new HttpException(
            {
              message: 'Validation failed',
              errors: errors,
            },
            HttpStatus.UNPROCESSABLE_ENTITY,
          );
        }

        //Cache rest of the errors and throw it with 500 server error.
        throw new HttpException(
          {
            message: 'Something went wrong',
          },
          HttpStatus.INTERNAL_SERVER_ERROR,
        );
      }
    }
  }

Enter fullscreen mode Exit fullscreen mode

Here, we create a class Validator which is a custom Nestjs pipes (read more about pipes).
In the constructor function we initiated the YUP schema.
Now, As we implement the PipeTransform interface, we have to use transform method and which will be called By Nestjs automatically.

On validation failed

Given system will throw the error with this formate.

{
  "message": "Validation failed",
  "errors": {
    "title": [
      "title is a required field",
      "title must be at least 3 characters"
    ],
    "description": [
      "description is a required field"
    ]
  }
}

Enter fullscreen mode Exit fullscreen mode

5. Use the Validator Pipe

Let’s validate the request of store method.

todo.controller.ts

import { Body, Controller, Get, Post, UsePipes } from '@nestjs/common';
import { TodoService } from './todo.service';
import { Todo } from './todo.interface';
import { createTodoDto, createTodoSchema } from './dto/create.dto';
import { Validator } from '../../Pipes/validator.pipe';

@Controller('todos')
export class TodoController {
  constructor(private readonly todoService: TodoService) {}

  @Get()
  async index(): Promise<Todo[]> {
    return await this.todoService.findAll();
  }

  @Post()
  //We can use our Custom Validator Pipes with Nestjs @UsePipes decorator. 

  @UsePipes(new Validator(createTodoSchema))
  async store(@Body() todoDto: createTodoDto): Promise<ApiResponse<Todo>> {
    console.log(todoDto);
    const todo = await this.todoService.create(todoDto);
    return {
      message: 'Todo Created Updated!',
      payload: todo,
    };
  }
}
Enter fullscreen mode Exit fullscreen mode

Explanation: In the @UsePipes decorator we passed our Validator class instance with the createTodoSchema schema and the Validator class will validate the request with defines rull on createTodoSchema.

todo.service.ts

import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';
import { Todo } from './todo.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { createTodoDto } from './dto/create.dto';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class TodoService {
  constructor(
    @InjectRepository(Todo)
    private todoRepository: Repository<Todo>,
    private config: ConfigService,
  ) {}

  findAll(): Promise<Todo[]> {
    console.log(this.config.get('app.name'));
    return this.todoRepository.find();
  }

  create(todoDto: createTodoDto): Promise<Todo> {
    const todo = new Todo();
    todo.title = todoDto.title;
    return this.todoRepository.save(todo);
  }
}

Enter fullscreen mode Exit fullscreen mode

Thanks for reading. If you want to read more about Custom Pipes please read the Nestjs official docs here.

Top comments (0)