Preface
In modern web development, data verification is an indispensable part. It not only ensures the accuracy of data, but also improves the security of the system. When using the NestJS framework for project development, the two libraries class-validator and class-transformer provide us with convenient data verification solutions.
This article will use detailed steps and practical skills to guide you how to use class-validator for data verification in NestJS. Through this article, you will be able to learn how to use class-validator to elegantly implement data verification, as well as 11 commonly used verification techniques in actual combat to improve the data verification ability of the project.
Steps to use
Step 1: Install class-validator and class-transformer
To use class-validator, two libraries need to be installed: class-validator and class-transformer.
npm install class-validator class-transformer
Step 2: Create DTO (Data Transfer Object)
In NestJS, DTO (Data Transfer Object) is usually used to define the structure of requested data. First, you need to create a DTO class for user registration and use the class-validator decorator to define the validation rules.
// src/user/dto/ import { IsString, IsEmail, IsNotEmpty, Length } from 'class-validator'; export class CreateUserDto { @IsString() @IsNotEmpty() @Length(4, 20) username: string; @IsEmail() email: string; @IsString() @IsNotEmpty() @Length(8, 40) password: string; }
In this DTO, three fields are defined: username, email, and password, and the verification rules are specified using the class-validator's decorator.
Step 3: Use pipeline to verify data
Next, you need to use DTO in the controller and verify the incoming data through NestJS's pipeline (Pipes).
// src/user/ import { Controller, Post, Body } from '@nestjs/common'; import { CreateUserDto } from './dto/'; @Controller('user') export class UserController { @Post('register') async register(@Body() createUserDto: CreateUserDto) { // Handle registration logic return { message: 'User registered successfully', data: createUserDto }; } }
In this example, the @Body() decorator is used in the register method to get the request body and pass in CreateUserDto. NestJS automatically verifies the DTO, and if the verification fails, an exception is thrown and an appropriate error response is returned.
Step 4: Enable the verification pipeline globally
For more convenient management, the verification pipeline can be enabled globally so that all DTO verifications will be performed automatically.
// src/ import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import { AppModule } from './'; async function bootstrap() { const app = await (AppModule); (new ValidationPipe()); await (3000); } bootstrap();
In the file, the verification pipeline is enabled globally using ValidationPipe. This way, NestJS will automatically perform data verification no matter which controller you use DTO. Of course, you can only enable the verification pipeline for some controllers. For details, please refer to the practical skills below.
Practical usage skills
1. Local verification pipeline
Verification pipelines can be configured for specific routing or controller methods without global activation. This allows for flexibly using different verification rules in different scenarios.
import { Controller, Post, Body, UsePipes, ValidationPipe } from '@nestjs/common'; import { CreateUserDto } from './dto/'; @Controller('user') export class UserController { @Post('register') @UsePipes(new ValidationPipe({ transform: true, whitelist: true, forbidNonWhitelisted: true, })) async register(@Body() createUserDto: CreateUserDto) { // Handle registration logic return { message: 'User registered successfully', data: createUserDto }; } }
2. Custom error message
class-validator allows custom error messages to be defined for each validation rule. For example:
import { IsString, IsNotEmpty, Length, IsEmail } from 'class-validator'; export class CreateUserDto { @IsString({ message: 'The username must be a string' }) @IsNotEmpty({ message: 'Username cannot be empty' }) @Length(4, 20, { message: 'The username must be between 4 and 20 characters' }) username: string; @IsEmail({}, { message: 'The email format is incorrect' }) email: string; @IsString({ message: 'The password must be a string' }) @IsNotEmpty({ message: 'Password cannot be empty' }) @Length(8, 40, { message: 'The password must be between 8 and 40 characters' }) password: string; }
3. Nested Object Verification
If the DTO contains nested objects, you can use the @ValidateNested() decorator for verification. For example:
import { Type } from 'class-transformer'; import { ValidateNested, IsString, IsNotEmpty } from 'class-validator'; class AddressDto { @IsString() @IsNotEmpty() street: string; @IsString() @IsNotEmpty() city: string; } export class CreateUserDto { @IsString() @IsNotEmpty() username: string; @ValidateNested() @Type(() => AddressDto) address: AddressDto; }
4. Combination Verification Decorator
Sometimes multiple validation rules may need to be combined together, and you can use @ValidatorConstraint() to create a custom validation decorator. For example:
Determine whether the user name already exists in the database
import { registerDecorator, ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface } from 'class-validator'; @ValidatorConstraint({ async: false }) export class IsUsernameUniqueConstraint implements ValidatorConstraintInterface { validate(username: any) { // Here you can add verification logic, such as querying the database return true; // If verification passes, return true } } export function IsUsernameUnique(validationOptions?: ValidationOptions) { return function (object: Object, propertyName: string) { registerDecorator({ target: , propertyName: propertyName, options: validationOptions, constraints: [], validator: IsUsernameUniqueConstraint, }); }; } // Use a custom decoratorexport class CreateUserDto { @IsUsernameUnique({ message: 'The username already exists' }) username: string; }
2. Verify password strength
import { registerDecorator, ValidationOptions, ValidationArguments } from 'class-validator'; export function IsStrongPassword(validationOptions?: ValidationOptions) { return function (object: Object, propertyName: string) { registerDecorator({ name: 'isStrongPassword', target: , propertyName: propertyName, options: validationOptions, validator: { validate(value: any, args: ValidationArguments) { return /(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}/.test(value); }, defaultMessage(args: ValidationArguments) { return 'The password must contain upper and lower case letters, numbers and special characters and is at least 8 characters long'; }, }, }); }; } export class ChangePasswordDto { @IsStrongPassword({ message: 'The password does not meet the strength requirements' }) newPassword: string; }
3. Condition verification
Verify based on the conditions, you can use the @ValidateIf decorator.
import { ValidateIf, IsNotEmpty, IsEmail } from 'class-validator'; export class UpdateUserDto { @IsEmail() email: string; @ValidateIf(o => ) @IsNotEmpty({ message: 'The new email address cannot be empty' }) newEmail: string; }
4. Use @Matches for regular expression verification
Using the @Matches decorator, you can verify that the string matches the specified regular expression.
import { Matches } from 'class-validator'; export class ChangePasswordDto { @Matches(/(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}/, { message: 'The password must contain upper and lower case letters, numbers and special characters and is at least 8 characters long' }) newPassword: string; }
5. Global verification options
When enabling the verification pipeline globally, you can configure global verification options, such as stripping non-whitelist fields, automatic conversion types, etc.
// src/ import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import { AppModule } from './'; async function bootstrap() { const app = await (AppModule); (new ValidationPipe({ whitelist: true, // Strip the non-whitelist field forbidNonWhitelisted: true, // Non-whitelist fields are prohibited transform: true, // Automatic conversion type })); await (3000); } bootstrap();
6. Dynamic verification message
Sometimes it may be necessary to generate error messages dynamically based on specific verification conditions, which can be implemented using ValidationArguments.
import { IsString, MinLength, ValidationArguments } from 'class-validator'; export class CreateUserDto { @IsString() @MinLength(4, { message: (args: ValidationArguments) => { return `The username is too short,At least it is required ${[0]} Characters`; }, }) username: string; }
7.@Validate Custom Verification Logic
If the built-in decorator does not meet the needs, you can use the @Validate decorator to add custom verification logic.
import { Validate, ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments } from 'class-validator'; @ValidatorConstraint({ name: 'customText', async: false }) class CustomTextConstraint implements ValidatorConstraintInterface { validate(text: string, args: ValidationArguments) { return ('prefix_'); // Any custom logic } defaultMessage(args: ValidationArguments) { return 'text ($value) Must be "prefix_" beginning'; } } export class CustomTextDto { @Validate(CustomTextConstraint) customText: string; }
8. Attribute grouping verification
By grouping, different fields can be verified in different situations. For example, different fields may need to be validated when creating and updating.
import { IsString, IsNotEmpty } from 'class-validator'; export class CreateUserDto { @IsString() @IsNotEmpty({ groups: ['create'] }) username: string; @IsString() @IsNotEmpty({ groups: ['create', 'update'] }) password: string; } // Specify the group when usingimport { ValidationPipe } from '@nestjs/common'; const createUserValidationPipe = new ValidationPipe({ groups: ['create'] }); const updateUserValidationPipe = new ValidationPipe({ groups: ['update'] }); Verify using different pipelines in the controller: import { Controller, Post, Put, Body, UsePipes } from '@nestjs/common'; import { CreateUserDto } from './dto/'; import { createUserValidationPipe, updateUserValidationPipe } from './validation-pipes'; @Controller('user') export class UserController { @Post('create') @UsePipes(createUserValidationPipe) async createUser(@Body() createUserDto: CreateUserDto) { // Handle the creation of user logic return { message: 'User created successfully', data: createUserDto }; } @Put('update') @UsePipes(updateUserValidationPipe) async updateUser(@Body() updateUserDto: CreateUserDto) { // Handle update user logic return { message: 'User updated successfully', data: updateUserDto }; } }
9. Only perform some attribute verification
Sometimes it may be necessary to verify only a portion of the properties of the object, which can be implemented using PartialType.
import { PartialType } from '@nestjs/mapped-types'; export class UpdateUserDto extends PartialType(CreateUserDto) {} //The following is how to use UpdateUserDto in the controller.// src/user/ import { Controller, Post, Put, Body, Param } from '@nestjs/common'; import { CreateUserDto } from './dto/'; import { UpdateUserDto } from './dto/'; @Controller('user') export class UserController { @Post('create') async createUser(@Body() createUserDto: CreateUserDto) { // Handle the creation of user logic return { message: 'User created successfully', data: createUserDto }; } @Put('update/:id') async updateUser(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) { // Handle update user logic return { message: 'User updated successfully', data: updateUserDto }; } }
In this example, UpdateUserDto inherits from PartialType(CreateUserDto), which means that UpdateUserDto contains all the properties in CreateUserDto, but these properties are optional. This is very useful in update operations, because we may want to provide only those fields that need to be updated, not all fields.
10. Internationalization of verification messages
The internationalization of verification messages can be achieved by using custom verification decorators and message generation functions.
import { IsString, IsNotEmpty, Length, ValidationArguments } from 'class-validator'; import { i18n } from 'i18next'; // Assume that i18n is used in the project export class CreateUserDto { @IsString() @IsNotEmpty({ message: (args: ValidationArguments) => ('') }) @Length(4, 20, { message: (args: ValidationArguments) => ('', { min: 4, max: 20 }) }) username: string; }
Summarize
Using class-validator combined with NestJS allows easy data verification in applications, which not only improves the readability of the code, but also ensures the accuracy and security of the data.
The above is the detailed content of NestJS using class-validator for data verification. For more information about NestJS data verification, please follow my other related articles!