DEV Community

Nithin K Joy
Nithin K Joy

Posted on

How to use class-validator and generate custom error object in nest.js

Objective

The main objective of this article is showing how to generate custom error object when the validation fails, but we will go through the process of using class-validator and later about generating custom error object.

What is class-validator

class-validator allows use of decorator and non-decorator based validation. Internally uses validator.js to perform validation. class-validator works on both browser and node.js platforms.

How to use it

In order to use class-validator in nest.js we need to install class-validator and class-transformer.
npm i --save class-validator class-transformer

To configure, in main.ts file we need to invoke ValidationPipe() constructor function imported form @nestjs/common and pass it as a argument for app.useGlobalPipes method.



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

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

bootstrap();


Enter fullscreen mode Exit fullscreen mode

{ stopAtFirstError: true } is just an optional argument to stop after getting the first error and return it. we can always customize it.

main.ts file

Let us assume we have a users resource. We need a create create-user.dto.ts file inside a dto folder under users resource as in image given below.

create-user.to.ts file location

Inside create-user.dto.ts file add the class validation you need to add. Refer class-validator docs for which validation you need to apply. docs: https://www.npmjs.com/package/class-validator

This is my create-user.dto.ts file



import {
  IsNotEmpty,
  IsString,
  MinLength,
  IsAlpha,
  Matches,
  IsEnum,
} from 'class-validator';

export enum UserState {
  ACTIVE = 'Active',
  IN_ACTIVE = 'Inactive',
}

export class CreateUserDto {
  @IsNotEmpty()
  @IsString()
  @IsAlpha()
  @MinLength(1)
  readonly firstName: string;

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

  @IsNotEmpty()
  @IsString()
  @IsNotEmpty()
  @Matches(/^[\w-\.]+@([\w-]+\.)+[\w-]{2,3}$/g)
  readonly email: string;

  @IsNotEmpty()
  @IsString()
  @Matches(/^[6789]\d{9}$/)
  readonly phoneNumber: string;

  @IsNotEmpty()
  @IsString()
  @MinLength(3)
  readonly companyName: string;

  @IsEnum(UserState)
  readonly userState: UserState;
}



Enter fullscreen mode Exit fullscreen mode

Next import CreateUserDto class and use it inside the user controller.



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

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

  @Post()
  create(@Body() user: CreateUserDto) {
    return this.usersService.create(user);
  }
}



Enter fullscreen mode Exit fullscreen mode

After sending request in postman or any other api testing application this will automatically validate and send error message as shown below
Request body



{
  "firstName":"",
  "lastName":"K Joy",
  "email":"Nn@gail.com",
  "phoneNumber":"9160000000",
  "companyName":"abcgf",
  "userState":"Active"
}


Enter fullscreen mode Exit fullscreen mode

Response



{
  "statusCode": 400,
  "message": [
    "firstName must be longer than or equal to 1 characters"
  ],
  "error": "Bad Request"
}


Enter fullscreen mode Exit fullscreen mode

image of a non customized class-validator

How to generate custom error message object

But we need to get which property just gave us this error along with the error message. In order to do that we need to configure the ValidationPipe() in main.ts file with some configurations.



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

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(
    new ValidationPipe({
      exceptionFactory: (errors) => {
        const result = errors.map((error) => ({
          property: error.property,
          message: error.constraints[Object.keys(error.constraints)[0]],
        }));
        return new BadRequestException(result);
      },
      stopAtFirstError: true,
    }),
  );
  await app.listen(3500);
}

bootstrap();


Enter fullscreen mode Exit fullscreen mode

Let me break down the above code. The object below is the error object.



{
  property: 'firstName',
  children: [],
  constraints: {
    minLength: 'firstName must be longer than or equal to 1 characters'
  }
}


Enter fullscreen mode Exit fullscreen mode

property: error.property usage is straight forward.
message is basically the value inside the the constraints object. But the key for the constraint object (here minLength) is not static. The key will be different for different validation failure.

Object.keys(error.constraints)[0] Object.keys just changes all object keys to array and we access the first key by [0].

Request body



{
  "firstName":"",
  "lastName":"K Joy",
  "email":"Nn@gail.com",
  "phoneNumber":"9160000000",
  "companyName":"abcgf",
  "userState":"Active"
}


Enter fullscreen mode Exit fullscreen mode

Response



{
  "statusCode": 400,
  "message": [
    {
      "property": "firstName",
      "message": "firstName must be longer than or equal to 1 characters"
    }
  ],
  "error": "Bad Request"
}


Enter fullscreen mode Exit fullscreen mode

If you found this helpful, be sure to leave a 💖 on the post. Thank you!

Top comments (9)

Collapse
 
davistran86 profile image
davistran86

Thanks alot, custom error message is what I'm looking for :)

Collapse
 
nithinkjoy profile image
Nithin K Joy

You're welcome

Collapse
 
nmhung2022 profile image
Simple is the best

I recommend using 422 UNPROCESSABLE ENTITY for validation errors.

app.useGlobalPipes(
    new ValidationPipe({
      exceptionFactory: (errors) => {
        const result = errors.map((error) => ({
          property: error.property,
          message: error.constraints[Object.keys(error.constraints)[0]],
        }));
       + return new UnprocessableEntityException(result);
       - return new BadRequestException(result);
      },
      stopAtFirstError: true,
    }),
  );
Enter fullscreen mode Exit fullscreen mode
Collapse
 
hassen profile image
mohamed

i need to custom like this
{
"statusCode": 400,
"message": "firstName must be longer than or equal to 1 characters"
}

any idea please

Collapse
 
russocorp profile image
Rafael Rossi

Tks sir

Collapse
 
baaliboudjaamaa profile image
Baali Boudjemaa

Thanks alot, can u share a github link to this project

Collapse
 
edsonlima95 profile image
Edson lima

great job man! thanks from brazil

Collapse
 
mundzirmuin profile image
mundzirmuin

This really saves me, thanks!

Collapse
 
vgeruso profile image
Victor Geruso Gomes

Amazing! Thanks!