background
Nest (NestJS) is a framework for building high-performance, scalable Node.js server applications. It uses progressive JavaScript, has full Typescript built-in support (but developers can program in pure JavaScript), and combines OOP (object-oriented programming), FP (functional programming), and FRP (responsive programming) principles.
Controller
In traditional Node.js server applications, controller is used to handle client requests corresponding to route. Each controller can have multiple routes, and different routes will perform different actions. In Nest, you can use built-in decorators to customize elements like Request, Response, Controller, Route, action, and so on. Thus, the repetitive code is removed and the development efficiency is improved. As follows:
// omit the dependency @controller ('cats') // Module name, used to define route prefix: /catsexportClass CatsController {// Change the status code of the route to 204@httpCode (204) // change the response Header @header ('Cache-Control'.'none'// Modify the route ([POST] /cats) request method: Create (@body () createCatDto: @body () createCatDto: @body () createCatDto: CreateCatDto) {// Returns data for the responsereturn 'This action adds a new cat';
}
@Redirect("http://www.fzzf.top"// Route redirection @get (":id") // [GET] /cats/:id
show(@Query("") query: ListAllEntities) {
// do something
}
}
Copy the code
For controller returns, Nest supports promises returned with async/await and Observable Streams returned with RxJS, as shown in the following example:
import { of } from 'rxjs'; // controller code omitted @put (":id")
async update(@Param() params: UpdateCatDto): Promise<any> {
returnthis.catsService.update(params); } // Async @get () index(@query () Query: IndexCatDto): Observable<any[]> {return of(this.catsService.find(query));
}
Copy the code
Provider
In Nest, a provider is defined as a class that has a set of methods for a particular function. The @Injectable() decorator makes it a service that can be injected into the Nest Module as a dependency. Between providers in the same scope, one provider can also be injected as a dependency into the other provider. The following code looks like this:
// cats.service
import { Injectable } from '@nestjs/common';
@Injectable()
export class CatsService {
constructor(
private readonly otherService: OtherService
)
async create(values: any): Promise<any> {
returnthis.otherService.create(values); }}Copy the code
We can declare a Module (see the next section) and inject the provider into the Controller. The details of dependency injection are done internally within the Nest framework, and here are just the basic dependency injection postures, as shown in the following code:
// cats.module.ts
import { Module } from '@nestjs/common';
import { CatsController } from './cats/cats.controller';
import { CatsService } from './cats/cats.service'; @Module({controllers: [CatsController], // Controller providers of the current Module: [CatsService], // Provider})export class CatModule {}
// cats.controller
import { CatsService } from './cats/cats.service';
@Controller('cats')
export class CatsController {
constructor(
private readonlyCatsService: catsService // Dependency injection) {} @post () create(@body () createCatDto: createCatDto) {// Returns data for the responsereturnthis.catsService.create(createCatDto); }}Copy the code
Module
Module is the basic unit of Nest application. Each Nest app has at least one Module (root Module). To put it simply, in Nest, n functional modules form a business module through dependency injection, and N business Modules form a Nest application. Both the Controller and provider need to be registered with the Module to complete dependency injection. The following code describes the basic Module declaration posture.
@Module({// Dependencies on other Module classes imports: [], // The controller class that business modules need to see how Module is instantiated as a business Module or a function Module controller: [], // provides the dependent class provider: [], // exports the module provider, can be injected by other module dependencies: [],})export class AppModule {}
Copy the code
This allows hierarchical dependencies between modules and providers in different modules to be reused by passing the exports argument. As shown in the following code:
Request.module. ts @ module ({// register provider provider: [RequestService], // export registered Provider exports: [RequestService],})exportClass RequestModule {} // api.module.ts @ module ({// import a RequestModule and Nest will register itexportProvider imports: [RequestModule], // Register Controller Controllers: [ApiController],})export class ApiModule {}
// api.controller.ts
@Controller('api')
export class ApiController {
constructor(
private readonlyDependency injection (dependency injection) {}}Copy the code
In addition, we can decorate Module A as A Global module with the @global decorator, so that any module can inject the export dependency in Module A without import. We can also import modules dynamically with specific parameters.
Middleware
Nest is a superframework, and the bottom layer relies on the Express framework (or HAPI), so the Middleware in Nest is equivalent to the Middleware in Express, and you can use it to access request and response, as well as do some pre – and post-processing. It is declared as follows:
- The statement
Import {Injectable, NestMiddleware} from'@nestjs/common';
import { Request, Response } from 'express';
@Injectable()
export class CatchMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: Function) {
console.log('catch... '); next(); }} // Mode 2export functioncatchError(req, res, next) { console.log(`catch... `); next(); };Copy the code
- use
// mode 1 @Module({imports: [CatsModule],})exportclass AppModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer .apply(CatchMiddleware) .forRoutes(CatsController); }} // const app = await nestfactory.create (AppModule); app.use(catchError); await app.listen(3000);Copy the code
Exception filters
Nest comes with an exception-filter layer that the handler doesn’t handle, as well as friendly exception handling objects. We throw catch global or local exceptions, such as global error, by declaring exception. As shown in the following code:
http-exception.filter.ts
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Request, Response } from 'express'; // if @catch () is null, Catch all types of exceptions @catch (HttpException)exportclass HttpExceptionFilter implements ExceptionFilter { catch(exception: HttpException, host: ArgumentsHost) {// Get the current handler's context object 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, }); }} // Use @post () @usefilters (new HttpExceptionFilter()) async create(@body () createCatDto: CreateCatDto) { throw new ForbiddenException(); }Copy the code
At the same time, we can inherit the Nest built-in exception object to standardize the format of system exceptions, as shown in the following code:
export class ForbiddenException extends HttpException {
constructor() {
super('Forbidden', HttpStatus.FORBIDDEN); }} // Error print {"status": 403,
"error": "This is a custom message"
}
Copy the code
Pipes
Pipe is used in Nest to pre-process requests from handlers in Controller.
- Format request input, such as req.query, req.param, req.body, etc.
- Validates the request input, passing it as is if it is correct, or throwing an exception anyway
exception
; You can refer to the declaration hereThe official documentation.
Guards
In Nest, Guard is designed to have a single blame pre-handler that informs Route if the corresponding handler needs to be executed. The official documentation recommends using Guard for authorization-related functions, such as permission validation and role validation, instead of using Middleware in traditional Express applications.
Middleware is officially considered inherently “dumb” because it has no way of knowing which handler will be executed after next is called. Guard can use ExecutionContext to get the ExecutionContext of the current request and know which handler is going to execute, thus ensuring that the developer adds processing logic in the correct request or response cycle.
It is declared as follows:
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs'; // Notice the function declaration @injectable ()export class AuthGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const request = context.switchToHttp().getRequest();
returnvalidateRequest(request); }}Copy the code
The Guard also supports global, single controller, or single Handle Method preprocessing. Please refer to the official documentation for details.
Interceptors
In the Nest, the Interceptor design inspiration comes from [AOP] (https://en.wikipedia.org/wiki/Aspect-oriented_programming), Used to add pre/post operations to a handler module, such as controller. Implement functions such as additional operations on input/output, filtering of exceptions, overloading the current handler logic under certain conditions, and so on. It is declared as follows:
import {
Injectable,
NestInterceptor,
ExecutionContext,
CallHandler,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable()
exportclass HttpInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable<any> {// context Current execution context // next next handler to executereturnnext.handle().pipe( map((value: any) => ({ code: 0, data: value, })) ); UseInterceptors(HttpInterceptor)export class CatsController {}
Copy the code
Request Lifecycle
The various Nest components described above are registered and managed by the Nest framework, and they are executed during a claim cycle phase of a request. The specific execution sequence is as follows:
- Request input
- global
middleware
- The root module (
root module
)middleware
- global
Guards
Controller Guards
route
Used on the corresponding handler function@UseGuards()
registeredGuard
- global
interceptors
(Controller pre-processing) controller
theinterceptors
(Controller pre-processing)route
Used on the corresponding handler function@UseInterceptors()
registeredinterceptors
(Controller pre-processing)- global
pipes
controller
thepipes
route
Used on the corresponding handler function@UsePipes()
registeredPipes
route
Registered for the handler function parameterPipes
(such as: @ Body (new ValidationPipe ())route
Corresponding processing functionroute
Corresponding to handler function dependenciesSerivce
route
Used on the corresponding handler function@UseInterceptors()
registeredinterceptors
(Controller post-processing)controller
theinterceptors
(Controller post-processing)- global
interceptors
(Controller post-processing) route
Registered for the handler functionException filters
controller
registeredException filters
- Globally registered
Exception filters
- The response output
The resources
- docs.nestjs.com/
- Reactivex. IO/RXJS/class /…