This article assumes that you are familiar with NestJS framework.
The Importance of Validation
Validation is a crucial step in ensuring the integrity and security of your application. While NestJS
documentation offers the class-validator library for validation, it can sometimes be challenging to work with. Lets explore an alternative validation approach using Yup in a NestJS
project.
Using class-validator
In a typical NestJS
project, you might define your DTOs (Data Transfer Objects) with class-validator
decorators to perform validation. In my project users can submit posts with title and variable amount of teams. A team has one field - title. In accordance with Nestjs
docs I created a DTO for post data and added validation to it:
class Team {
@IsDefined({message: 'Bad request'})
@IsString({message: 'Bad request'})
@MinLength(TITLE_MIN_LENGTH, {
message: 'Too short',
})
@MaxLength(TITLE_MAX_LENGTH, {
message: 'Too long',
})
name: string;
}
class PostBody {
@IsDefined({message: 'Bad request'})
@IsString({message: 'Bad request'})
@MinLength(TITLE_MIN_LENGTH, {
message: 'Too short',
})
@MaxLength(TITLE_MAX_LENGTH, {
message: 'Too long',
})
title: string;
@IsDefined({message: 'Bad request'})
@IsArray({message: 'Bad request'})
@ArrayMinSize(MIN_TEAMS)
@ArrayMaxSize(MAX_TEAMS)
@ValidateNested()
@Type(() => Team)
teams: Team[];
}
Pretty hard to find where the real DTO is, huh?
My primary concern was that the ArrayMinSize
and ArrayMaxSize
validators were not functioning correctly. Users could submit any number of teams, and the DTO would still pass validation. This is a known issue with class-validator
, and although workarounds exist, none of them seemed optimal.
Introducing Yup
After conducting some research I decided to switch to a library that works for all of my use cases out of box. I have looked at most popular validation libraries and found out that Yup
covers all my needs:
- Validates my DTOs.
- Easy schema definition syntax.
- Ability to incorporate error messages directly into validation rules.
Yup
functions similarly to class-validator
. It requires you to define validation rule schemas, allowing to separate the DTO from validation. The DTO is now a plain JavaScript object:
class PostBody {
title: string;
teams: Team[];
}
Validation rules can be defined as constants and can be reused across various validation scenarios. Lets define rule for validating title:
const titleSchema = string()
.strict()
.typeError('Bad request')
.trim()
.min(TITLE_MIN_LENGTH, `Too short`)
.max(TITLE_MAX_LENGTH, `Too long`)
And now to the main point - validation for an array of objects:
const teamsSchema = array()
.strict()
.typeError('Bad request')
.min(MIN_TEAMS, 'Bad request')
.max(MAX_TEAMS, 'Bad request')
.of(
object({
name: titleSchema,
}),
)
Schema for validating the entire post:
const postSchema = object({
title: titleSchema,
teams: teamsSchema,
})
Note the reuse of titleSchema
in postSchema
and in teamsSchema
. DRY in action.
Declaring validation rules as constants enables us to create similar schemas without the need to redundantly redeclare the rules. For instance, we can define a "post create" schema that accepts all fields and a "post update" schema that accepts only the title field.
To integrate Yup
validation into a NestJS
project, we'll need a custom validation pipe:
class YupValidationPipe implements PipeTransform {
constructor(private schema: ObjectSchema<any>) {}
transform(value: any) {
return this.schema.validateSync(value);
}
}
Now we can use the YupValidationPipe
in a controller action to validate incoming data easily:
async createPost(
@GetUser() user: User,
@Body(new YupValidationPipe(postSchema)) post: PostBody
) { ... }
Conclusion
While initially seeking a library solely for required validation, my transition to Yup
yielded unexpected advantages:
- Cleaner and more concise DTO code.
- Rule reusability.
- Convenient error message handling within validation rules, facilitating user-friendly error displays (an uncommon feature in many libraries).
Of course, integrating Yup
into my NestJS
project required some additional effort, but it ultimately proved to be a more efficient solution compared to troubleshooting issues with class-validator
.
Visit clapduel.com to see the code in action.
Top comments (0)