Validation of input is a basic function of a Web application, not only at the front end, but also at the back end:

  • Front-end validation can avoid unnecessary requests and give feedback to users as quickly as possible
  • Backend authentication can prevent some malicious submissions that bypass the browser

You don’t have to write validation on the front end. There are a lot of validation libraries, and you write a lot of validation. Nest.js is a framework for verifying parameters in the nest.js framework.

Here’s what you’ll learn:

  • The nest.js pipe does the validation and conversion of parameters
  • The Exception filter of Nest.js handles the exception and returns a response
  • Nest.js uses class-validation for declarative parameter validation

Nest. Js

Nest.js is a back-end framework based on IOC and MVC ideas:

  • MVC is a layer of Controller, Service, and Repository, which is a common architecture for back-end frameworks
  • IOC is a dependency injection. In other words, Controller, Service, Repository and other instances can be injected automatically in IOC containers. You only need to declare dependencies without manual new.

In addition, Nest.js also supports modules, which can package Controller, Service, and Repository into a Module for easy code organization.

The overall architecture is shown as follows:

The whole IOC container has multiple instances of Controller, Service, Respository, and so on, scattered in different modules. Have an AppModule as the root to introduce other modules.

Requests are handled in Controller, Service is called to do the business logic, and CRUD to the database is done by Repository.

So where should I put validate for the parameter?

Parameter validate implementation ideas

The verification of parameters can be done in the Controller, but the verification logic is universal, and it is too troublesome to do it in every Controller. Can you do it before the Controller?

Nest.js is a Pipe.

Nest.js supports pipes, which will be called before the request reaches the Controller, validate and convert arguments, and if an exception is thrown, it will not be passed to the Controller.

This pipeline feature is suitable for general logic across controllers, such as string int conversion, parameter validation, and so on.

Nest.js has eight built-in pipes:

  • ValidationPipe
  • ParseIntPipe
  • ParseBoolPipe
  • ParseArrayPipe
  • ParseUUIDPipe
  • ParseEnumPipe
  • ParseFloatPipe
  • DefaultValuePipe

It can be divided into 3 categories:

ParseXxx, which converts the parameter to some type; DefaultValue, set the parameter defaultValue. Validation does the validation of parameters.

These are very generic features.

Clearly, validation can be done with the ValidationPipe.

But instead of using the Pipe provided by Nest.js, try implementing it yourself.

Pipe is a class that implements the PipeTransform interface, implements its transform method, performs various conversions or validations on a value, and throws an exception if validation fails.

import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';

@Injectable(a)export class MyValidationPipe implements PipeTransform<any> {
  async transform(value: any, metadata: ArgumentMetadata) {
    if (value.age > 20) {
      throw new BadRequestException('Over age limit');
    } else {
      value.age += 10;
    }
    returnvalue; }}Copy the code

We then call useGlobalPipes to register the Pipe when the IOC container is started:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { MyValidationPipe } from './pipes/MyValidationPipe';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new MyValidationPipe());
  await app.listen(3000);
}
bootstrap();
Copy the code

Let’s test it out:

When age is greater than 20, an exception is thrown and a response is returned.

When the parameter is less than 20, the parameter is modified and passed to the Controller:

As you can see, the parameters are passed to the Controller and modified.

That’s what pipes are for.

Therefore, we simply validate the parameter in pipe. You can use the class-validation package, which supports decorators to configure validation rules:

Something like this:

import { IsEmail, IsNotEmpty, IsPhoneNumber, IsString } from "class-validator";

export class CreatePersonDto {
    @IsNotEmpty({
        message: 'Name cannot be null'
    })
    @IsString(a)name: string;

    @IsPhoneNumber("CN", {
        message: 'Phone is not a phone number'
    })
    phone: string;

    @IsEmail({}, {
        message: 'Email is not a legitimate email address'
    })
    email: string;
}
Copy the code

Then call the validate method in pipe and throw an exception if there is an error:

import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
import { validate } from 'class-validator';
import { plainToClass } from 'class-transformer';

@Injectable(a)export class MyValidationPipe implements PipeTransform<any> {
  async transform(value: any, { metatype }: ArgumentMetadata) {
    if(! metatype) {return value;
    }
    const object = plainToClass(metatype, value);
    const errors = await validate(object);
    if (errors.length > 0) {
      throw new BadRequestException('Validation failed');
    }
    returnvalue; }}Copy the code

Since we are configuring with decorators, we need to get the decorator of the corresponding class through the object, so before validate, we call the plainToClass method of the class-Transformer package to convert the plain parameter object into an instance of the class.

In this way, the function of parameter verification is realized:

This is how the Nest.js ValidationPipe works.

Of course, we didn’t do the wrong formatting. It’s not as beautiful as the built-in Pipe. Let’s take a look at the effect of the built-in Pipe:

Enable the built-in ValidationPipe:

import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe());
  await app.listen(3000);
}
bootstrap();
Copy the code

Then test it:

The format of this return is much better.

Also, did you notice that we only returned a BadRequestException error, but the server returned 400 corresponding, what is this reason?

This brings us to another mechanism of Nest.js: the Exception Filter.

Nex.js supports ExceptionFilter, which can declare the response to the error. In this way, the application only needs to throw the corresponding exception to return the response.

The ExceptionFilter takes the form of a class that implements the ExceptionFilter interface and uses the Catch decorator to declare what exception to handle. It implements its catch method, which takes a response object and returns the corresponding response.

Define an exception:

export class ForbiddenException extends HttpException {
    constructor() {
        super('Forbidden', HttpStatus.FORBIDDEN); }}Copy the code

Define exception filters:

import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Request, Response } from 'express';

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();
    const status = exception.getStatus();

    response
      .status(status)
      .json({
        statusCode: status,
        timestamp: new Date().toISOString(),
        path: request.url, }); }}Copy the code

Obviously, the built-in ExceptionFilter is the reason why we returned 400 ExceptionFilter in the ValidationPipe with a BadRequestException error.

Nest. Js has many ExceptionFilters built in, such as:

  • BadRequestException returns 400, indicating that the client passed an error parameter
  • ForbiddenException returns 403, indicating no permission
  • NotFoundException returns 404, indicating the resource was not found

ExceptionFilter can be returned with any exception you want. If not, you can customize ExceptionFilter.

At this point, we implement validate for the parameter, via Pipe + ExceptionFilter.

Code address: github.com/QuarkGluonP…

conclusion

Validation of input is a basic function, both front and back.

Let’s go over the basics of Nest.js: Nest.js is an MVC + IOC architecture and supports Modules to organize code.

Then, the implementation idea of validate of Nest.js is explored: the validation can be put before Controller, and the parameter is verified and converted through Pipe. If there is an error, the exception will be thrown, and the exception will trigger ExceptionFilter, so that different error response will be returned.

Pipe is called before the Controller, and if an exception is thrown, the request will not continue to the Controller.

ExceptionFilter can listen for different types of Exceptions and respond differently.

Built-in a lot of Pipe and ExceptionFilter can be directly used, not enough when you can also define their own.

Of course, if you’re just implementing validation, don’t bother and use the ValidationPipe.

Validation is a basic feature, but it’s useful because we learned Pipe and ExceptionFilter from it.