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
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
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>;
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,
);
}
}
}
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"
]
}
}
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,
};
}
}
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);
}
}
Thanks for reading. If you want to read more about Custom Pipes please read the Nestjs official docs here.
Top comments (0)