Validation Error Response

The Default 400 Response

Example

curl -i -X POST http://localhost:3000/products -H "Content-Type: application/json" \
  -d '{"name":"TV","price":-100,"stock":2.5,"category":"toys"}'

Example output

HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8

{
  "message": [
    "name must be longer than or equal to 3 characters",
    "price must be a positive number",
    "stock must be an integer number",
    "category must be one of the following values: audio, mobiles, laptops, wearables, accessories"
  ],
  "error": "Bad Request",
  "statusCode": 400
}

Custom Messages and a Custom Format

Each decorator takes a message. To change the whole response shape, pass an exceptionFactory:

import { BadRequestException, ValidationError, ValidationPipe } from '@nestjs/common';

// in a DTO
export class CreateProductDto {
  @IsPositive({ message: 'Price must be more than ₹0' })
  price: number;
}

// in main.ts
app.useGlobalPipes(
  new ValidationPipe({
    whitelist: true,
    transform: true,
    exceptionFactory: (errors: ValidationError[]) =>
      new BadRequestException({
        code: 'VALIDATION_FAILED',
        fields: Object.fromEntries(errors.map((e) => [e.property, Object.values(e.constraints ?? {})])),
      }),
  }),
);

Example

curl -X POST http://localhost:3000/products -H "Content-Type: application/json" -d '{"name":"TV","price":-100}'

Example output

{"code":"VALIDATION_FAILED","fields":{"name":["name must be longer than or equal to 3 characters"],"price":["Price must be more than ₹0"],"stock":["stock must not be less than 0","stock must be an integer number"],"category":["category must be one of the following values: audio, mobiles, laptops, wearables, accessories","category should not be empty"]}}

Exercise: Write a RegisterUserDto with: name (2–50 chars), email (valid email), phone (Indian mobile), password (min 8 chars, at least one digit).

Show answer
import { IsEmail, IsMobilePhone, IsString, Length, Matches, MinLength } from 'class-validator';

export class RegisterUserDto {
  @IsString()
  @Length(2, 50)
  name: string;

  @IsEmail()
  email: string;

  @IsMobilePhone('en-IN')
  phone: string;

  @IsString()
  @MinLength(8)
  @Matches(/\d/, { message: 'password must contain a number' })
  password: string;
}