Nest. js Parameter Checksum Custom Return Data Format Detailed Explanation

  • 2021-11-01 23:12:00
  • OfStack

0x0 Parameter Verification

Parameter check most of the business is to use Nest. js in the pipeline method to achieve, you can refer to the documentation. However, there are some problems in the writing process, although the document is obscure.

In doing a query interface, which contains 1 parameters to make dto structure data:


import { ApiProperty } from '@nestjs/swagger'

export class QueryUserDto {
 @ApiProperty({
 required: false,
 description: ' Page number '
 })
 readonly currentPage: number

 @ApiProperty({
 required: false,
 description: ' Number of articles '
 })
 readonly pageSize: number

 @ApiProperty({
 required: false,
 description: ' User account number '
 })
 readonly username?: string

 @ApiProperty({
 required: false,
 description: ' User status '
 })
 readonly activeStatus: number

 @ApiProperty({
 required: false,
 description: ' The way of sorting : ASC, DESC'
 })
 readonly order: 'DESC' | 'ASC'
}
 TYPESCRIPT

Pass in the corresponding parameters in @ Query request, and find that the obtained data types are all String, and then consult the relevant documents to understand that Type of class-transformer is needed for conversion:


import { ApiProperty } from '@nestjs/swagger'
import { Type } from 'class-transformer'

export class QueryUserDto {
 @ApiProperty({
 required: false,
 description: ' Page number '
 })
 @Type(() => Number)
 readonly currentPage: number = 1

 @ApiProperty({
 required: false,
 description: ' Number of articles '
 })
 @Type(() => Number)
 readonly pageSize: number = 10

 @ApiProperty({
 required: false,
 description: ' User account number '
 })
 readonly username?: string

 @ApiProperty({
 required: false,
 description: ' User status '
 })
 @Type(() => Number)
 readonly activeStatus: number = 3

 @ApiProperty({
 required: false,
 description: ' The way of sorting : ASC, DESC'
 })
 readonly order: 'DESC' | 'ASC' = 'DESC'
}

Then turn on the transform option in the ValidationPipe pipeline method:


app.useGlobalPipes(
 new ValidationPipe({
 transform: true
 })
)

Or in app. modules. ts:


import { ValidationPipe } from '@nestjs/common'
import { APP_PIPE } from '@nestjs/core'

@Module({
 imports: [
 // ...
 ],
 controllers: [AppController],
 providers: [
 {
  provide: APP_PIPE,
  useValue: new ValidationPipe({
  transform: true
  })
 }
 ]
})

The usage of the two is different from whether the application type of the program is mixed or not.

In order to save trouble, I write it directly in the global method, and the data finally obtained at service is the data processed by pipeline business, so there is no need to judge a lot of data types at service layer.

0x1 Custom Return Data Format

The data returned in controller is all from the database table structure:


{
 "id": "d8d5a56c-ee9f-4e41-be48-5414a7a5712c",
 "username": "Akeem.Cremin",
 "password": "$2b$10$kRcsmN6ewFC2GOs0TEg6TuvDbNzf1VGCbQf2fI1UeyPAiZCq9rMKm",
 "email": "Garrett87@hotmail.com",
 "nickname": "Wallace Nicolas",
 "role": "user",
 "isActive": true,
 "createdTime": "2021-03-24T15:24:26.806Z",
 "updatedTime": "2021-03-24T15:24:26.806Z"
}

If you need to define the data format of the final return interface, for example:


{
 "statusCode": 200,
 "message": " Achieve success ",
 "data": {
  "id": "d8d5a56c-ee9f-4e41-be48-5414a7a5712c",
  "username": "Akeem.Cremin",
  "password": "$2b$10$kRcsmN6ewFC2GOs0TEg6TuvDbNzf1VGCbQf2fI1UeyPAiZCq9rMKm",
  "email": "Garrett87@hotmail.com",
  "nickname": "Wallace Nicolas",
  "role": "user",
  "isActive": true,
  "createdTime": "2021-03-24T15:24:26.806Z",
  "updatedTime": "2021-03-24T15:24:26.806Z"
 }
}

Here you need to make a custom successful request interceptor:


nest g in shared/interceptor/transform

import { CallHandler, ExecutionContext, Injectable, Logger, NestInterceptor } from '@nestjs/common'
import { Observable } from 'rxjs'
import { map } from 'rxjs/operators'
import { Request } from 'express'

interface Response<T> {
 data: T
}

@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<T, Response<T>> {
 intercept(context: ExecutionContext, next: CallHandler<T>): Observable<any> {
 const request = context.switchToHttp().getRequest<Request>()
 Logger.log(request.url, ' Normal interface request ')

 return next.handle().pipe(
  map(data => {
  return {
   data: data,
   statusCode: 200,
   message: ' Request succeeded '
  }
  })
 )
 }
}

Then you can use it when introduced in app. module. ts:


import { ValidationPipe } from '@nestjs/common'
import { APP_INTERCEPTOR } from '@nestjs/core'

import { TransformInterceptor } from '@/shared/interceptor/transform.interceptor'

@Module({
 imports: [
 // ...
 ],
 controllers: [AppController],
 providers: [
 {
  provide: APP_INTERCEPTOR,
  useClass: TransformInterceptor
 }
 ]
})

However, attention should be paid to the sorting of APP_INTERCEPTOR, and TransformInterceptor is best placed in the first place, otherwise it will fail.

Error filter:


nest g f shared/filters/httpException

import { ApiProperty } from '@nestjs/swagger'
import { Type } from 'class-transformer'

export class QueryUserDto {
 @ApiProperty({
 required: false,
 description: ' Page number '
 })
 @Type(() => Number)
 readonly currentPage: number = 1

 @ApiProperty({
 required: false,
 description: ' Number of articles '
 })
 @Type(() => Number)
 readonly pageSize: number = 10

 @ApiProperty({
 required: false,
 description: ' User account number '
 })
 readonly username?: string

 @ApiProperty({
 required: false,
 description: ' User status '
 })
 @Type(() => Number)
 readonly activeStatus: number = 3

 @ApiProperty({
 required: false,
 description: ' The way of sorting : ASC, DESC'
 })
 readonly order: 'DESC' | 'ASC' = 'DESC'
}

0

It can then be used when introduced in app. module. ts:


import { ApiProperty } from '@nestjs/swagger'
import { Type } from 'class-transformer'

export class QueryUserDto {
 @ApiProperty({
 required: false,
 description: ' Page number '
 })
 @Type(() => Number)
 readonly currentPage: number = 1

 @ApiProperty({
 required: false,
 description: ' Number of articles '
 })
 @Type(() => Number)
 readonly pageSize: number = 10

 @ApiProperty({
 required: false,
 description: ' User account number '
 })
 readonly username?: string

 @ApiProperty({
 required: false,
 description: ' User status '
 })
 @Type(() => Number)
 readonly activeStatus: number = 3

 @ApiProperty({
 required: false,
 description: ' The way of sorting : ASC, DESC'
 })
 readonly order: 'DESC' | 'ASC' = 'DESC'
}

1

0x2 Hide a field in an entity class

Originally, I wanted to use @ Exclude attribute to hide some sensitive fields in the database, but I found that it could not meet the special needs. If I return a single instance, I can hide it, but I can't realize it if I have an findAll, which is very detailed in the document Serialization NestJS-A progressive Node. js framework, but there is another way here. First, add attributes to the strength class sensitive data field:


import { ApiProperty } from '@nestjs/swagger'
import { Type } from 'class-transformer'

export class QueryUserDto {
 @ApiProperty({
 required: false,
 description: ' Page number '
 })
 @Type(() => Number)
 readonly currentPage: number = 1

 @ApiProperty({
 required: false,
 description: ' Number of articles '
 })
 @Type(() => Number)
 readonly pageSize: number = 10

 @ApiProperty({
 required: false,
 description: ' User account number '
 })
 readonly username?: string

 @ApiProperty({
 required: false,
 description: ' User status '
 })
 @Type(() => Number)
 readonly activeStatus: number = 3

 @ApiProperty({
 required: false,
 description: ' The way of sorting : ASC, DESC'
 })
 readonly order: 'DESC' | 'ASC' = 'DESC'
}

2

select: false can hide this field when returning query results, but all queries involving this field must add this field, such as my user. service. ts login query:


const user = await getRepository(UserEntity)
   .createQueryBuilder('user')
   .where('user.username = :username', { username })
   .addSelect('user.password')
   .getOne()

. addSelect ('user. password') Adding this property query will include the field password, which would otherwise not be included in the methods of ordinary queries.

Summarize


Related articles: