NestJS build blog system (four) – use interceptor, exception filter to achieve a unified return format
preface
We implemented data persistence in the previous section, and now we have a working CURD module. In a real project, the server will wrap the data in a uniform return format for easy docking and friendly prompts.
Return structure
When you come into contact with a more comfortable interface format at work, here is a recommendation, there are better practices to share.
// Successful return
{
code: 200.data: {
/ / class for details
info: {
// Return data
},
/ / list
list: [].pagination: {
total: 100.pageSize: 10.pages: 10.page: 1,}},message: "Request successful"
},
// Return on failure
{
code: 400.message: "Query failed",}Copy the code
Code Using HTTP code can basically meet this requirement. Info is used to host the detail class, with multiple infos using different prefixes, such as userInfo, articleInfo. List is used for lists, and multiple lists refer to INFO. Pagination is used to carry paging information.
implementation
Request processing successful
Based on Nest’s lifecycle and documentation, successful requests can be wrapped in a post-request interceptor.
Creating interceptors
nest g in interceptor/transform
Copy the code
Modify the interceptor code
// src/interception/transform.interception.ts
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable(a)export class TransformInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next
.handle()
.pipe(
map(data= > ({
code: 200,
data,
message: 'success'}}})))Copy the code
Use a global interceptor in main
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { HttpExceptionFilter } from './filters/http-execption.filter';
import { TransformInterceptor } from './interceptor/transform.interceptor';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalInterceptors(new TransformInterceptor())
await app.listen(3000);
}
bootstrap();
Copy the code
Modify the getOne method of atricle.service
// src/modules/article/article.service.ts
async getOne(
idDto: IdDTO
) {
const { id } = idDto
const articleDetial = await this.articleRepository
.createQueryBuilder('article')
.where('article.id = :id', { id })
.getOne()
if(! articleDetial) {throw new NotFoundException('Unable to find article')}const result = {
info: infoarticleDetial,
}
return result
}
Copy the code
Request /atricle/info? id=1
You can see that the returned information matches our expectations
{
"code": 200."data": {
"info": {
"id": 1."createTime": "The 2021-06-29 T02:48:28. 623 z"."updateTime": "The 2021-06-29 T02:48:28. 623 z"."isDelete": false."version": 1."title": "Heading 1"."description": "1"."content": "1" for details}},"message": "success"
}
Copy the code
Failed to process request
We could have just used the basic exception class provided by Nest, but the format wasn’t what we wanted, so we’ll use an exception filter here.
NestJS provides the basic HTTP exception classes
class | meaning | Status code |
---|---|---|
BadRequestException | The server did not understand the client’s request and did not do any processing | 400 |
UnauthorizedException | The user does not provide authentication credentials or is not authenticated | 401 |
NotFoundException | The requested resource does not exist or is unavailable | 404 |
ForbiddenException | The user is authenticated, but does not have the required permissions to access the resource | 403 |
NotAcceptableException | unacceptable | 406 |
RequestTimeoutException | The request timeout | 408 |
ConflictException | conflict | 409 |
GoneException | The requested resource has been moved from this address and is no longer available | 410 |
PayloadTooLargeException | The load is too large | 413 |
UnsupportedMediaTypeException | The return format required by the client is not supported. For example, the API can only return JSON, but the client requires XML. | 415 |
UnprocessableException | The attachment uploaded by the client cannot be processed, causing a request failure | 422 |
InternalServerErrorException | Client request valid, server processing an accident | 500 |
NotImplementedException | Unrealized. | 501 |
BadGatewayException | Bad gateway | 502 |
ServiceUnavailableException | The server is unable to process the request, generally used for website maintenance status | 503 |
GatewayTimeoutException | Gateway timeout | 504 |
Blogging systems involve relatively little business, so just go with what Nest provides and unify the return format
Create nest F Filters /httpExecption interceptor
Modify the filters/HTTP – execption. Filters. Ts
// src/filters/http-execption.filters.ts
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Request, Response } from 'express';
import { execPath } from 'process';
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const status = exception.getStatus();
const message = exception.message
response
.status(status)
.json({
code: status, message, }); }}Copy the code
Use exception filters globally in main.ts
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { HttpExceptionFilter } from './filters/http-execption.filter';
import { TransformInterceptor } from './interceptor/transform.interceptor';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalInterceptors(new TransformInterceptor())
app.useGlobalFilters(new HttpExceptionFilter())
await app.listen(3000);
}
bootstrap();
Copy the code
/article/info? id=10000
{
"code": 404."message": "Can't find the article"
}
Copy the code
Now that our formatting is basically configured, let’s rewrite the other article methods
// src/modules/article/article.service.ts
import { Injectable, NotFoundException } from '@nestjs/common';
import { ArticleCreateDTO } from './dto/article-create.dto';
import { ArticleEditDTO } from './dto/article-edit.dto';
import { IdDTO } from './dto/id.dto';
import { ListDTO } from './dto/list.dto';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Article } from './entity/article.entity';
import { getPagination } from 'src/utils';
@Injectable(a)export class ArticleService {
constructor(
@InjectRepository(Article)
private readonly articleRepository: Repository<Article>,
) {}
/ * * * *@param listDTO
* @returns * /
async getMore(
listDTO: ListDTO,
) {
const { page = 1, pageSize = 10 } = listDTO
const getList = this.articleRepository
.createQueryBuilder('article')
.where({ isDelete: false })
.select([
'article.id'.'article.title'.'article.description'.'article.createTime'.'article.updateTime',
])
.skip((page - 1) * pageSize)
.take(pageSize)
.getManyAndCount()
const [list, total] = await getList
const pagination = getPagination(total, pageSize, page)
return {
list,
pagination,
}
}
/ * * * *@param idDto
* @returns * /
async getOne(
idDto: IdDTO
) {
const { id } = idDto
const articleDetial = await this.articleRepository
.createQueryBuilder('article')
.where('article.id = :id', { id })
.getOne()
if(! articleDetial) {throw new NotFoundException('Unable to find article')}return {
info: articleDetial
}
}
/ * * * *@param articleCreateDTO
* @returns * /
async create(
articleCreateDTO: ArticleCreateDTO
){
const article = new Article();
article.title = articleCreateDTO.title
article.description = articleCreateDTO.description
article.content = articleCreateDTO.content
const result = await this.articleRepository.save(article);
return {
info: result
}
}
/ * * * *@param articleEditDTO
* @returns * /
async update(
articleEditDTO: ArticleEditDTO
) {
const { id } = articleEditDTO
let articleToUpdate = await this.articleRepository.findOne({ id })
articleToUpdate.title = articleEditDTO.title
articleToUpdate.description = articleEditDTO.description
articleToUpdate.content = articleEditDTO.content
const result = await this.articleRepository.save(articleToUpdate)
return {
info: result,
}
}
/ * * * *@param idDTO
* @returns * /
async delete (
idDTO: IdDTO,
) {
const { id } = idDTO
let articleToUpdate = await this.articleRepository.findOne({ id })
articleToUpdate.isDelete = true
const result = await this.articleRepository.save(articleToUpdate)
return {
info: result
}
}
}
Copy the code
// src/utils/index.ts
/** * Compute paging *@param total
* @param pageSize
* @param page
* @returns * /
export const getPagination = (
total: number,
pageSize: number,
page: number) = > {
const pages = Math.ceil(total / pageSize)
return {
total,
page,
pageSize,
pages,
}
}
Copy the code
reference
- NestJS
- NestJS Chinese website
- Ruan Yifeng: RESTful API best practices
- Code for this section
A series of
- NestJS build blog system (a) – build a framework
- NestJS build blog system (2) – write static article CURD
- NestJS build blog system (3) – using TypeORM+Mysql to achieve data persistence