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.
"class-transformer": "^0.2.3",
"class-validator": "^0.10.0",
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;
}
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);
}
}
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.
DTO is used in order to validate incoming requests. The DTO on its own is more of a guideline for the developer and those who consume the API to know what kind of shape the request body expects to be, it doesn't actually run any validations on its own!!!.
A DTO is an object that defines how the data will be sent over the network. We could determine the DTO schema by using TypeScript interfaces, or by simple classes. Interestingly, we recommend using classes here.
The in-built NestJS ValidationPipe is a great way to handle validations in an application. Validations are an extremely important aspect of any production-level application. Often, developers have to write large amounts of code to handle even basic validations. This is largely counter-productive.
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With