'Validation does not work with Partial<DTO> - NestJS

I want to apply server-side validation on my CRUD API. The entity in question is called Employee. I am using an employee.dto (shown below) for the create and update endpoints.

The class-validator package works fine on the create method but ignores all rules in the DTO when I use it with Partial<EmployeeDTO> in the update method.

Please use the code below for reference.

Packages

"class-transformer": "^0.2.3",
"class-validator": "^0.10.0",

Employee DTO

import { IsString, IsNotEmpty, IsEmail, IsEnum } from 'class-validator';

import { EmployeeRoles } from '../../entities/employee.entity';

export class EmployeeDTO {
  @IsString()
  @IsEmail()
  @IsNotEmpty()
  email: string;

  @IsString()
  @IsNotEmpty()
  password: string;

  @IsString()
  @IsNotEmpty()
  username: string;

  @IsString()
  @IsNotEmpty()
  fullName: string;

  @IsString()
  @IsNotEmpty()
  @IsEnum(EmployeeRoles)
  role: string;
}

Employee Controller

import {
  Controller,
  Param,
  Post,
  Body,
  Put,
  UsePipes,
} from '@nestjs/common';

import { EmployeeDTO } from './dto/employee.dto';
import { EmployeeService } from './employee.service';
import { ValidationPipe } from '../shared/pipes/validation.pipe';

@Controller('employee')
export class EmployeeController {
  constructor(private employeeService: EmployeeService) {}

  @Post()
  @UsePipes(ValidationPipe)
  addNewEmployee(@Body() data: EmployeeDTO) {
    return this.employeeService.create(data);
  }

  @Put(':id')
  @UsePipes(ValidationPipe)
  updateEmployee(@Param('id') id: number, @Body() data: Partial<EmployeeDTO>) {
    return this.employeeService.update(id, data);
  }
}

Possible Solution

I work around I can think of is creating separate DTOs for create and update methods, but I don't like the idea of repeating the code.



Solution 1:[1]

For this answer, I'll take a guess and assume that you use the ValidationPipe provided in the NestJS' documentation, or a close derivative.

Your updateEmployee method's argument data type is Partial, which doesn't emit any type metadata. for the ValidationPipe to instantiate it using the class-transformer module, resulting in the class-validator module to validate a plain object, and not an EmployeeDTO.

For the validation to work, the type of the data argument should be a class. You could either make separate DTOs to create and update your entity, or use validation groups if you want to keep a single class.

Solution 2:[2]

In order to achieve partial validation, you can use PartialType utility function. You can read about it here: https://docs.nestjs.com/openapi/mapped-types#partial

You would need to create another class:

export class UpdateEmployeeDTO extends PartialType(EmployeeDTO) {}

and then in your controller, you need to replace the type of @Body data Partial<EmployeeDTO> to UpdateEmployeeDto. It should look like this:

@Patch(':id')
@UsePipes(ValidationPipe)
updateEmployee(@Param('id') id: number, @Body() data: UpdateEmployeeDTO) {
    return this.employeeService.update(id, data);
}

Please keep in mind that you should import PartialType from @nestjs/mapped-types not from @nestjs/swagger like suggested in the documentation. More about this can be found here

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Thibault Burger
Solution 2