nest入门教程
Nest是构建高效,可扩展的 Node.js Web 应用程序的框架
大纲
起步
安装nestjs
创建nest项目
- 目录如下:
1 2 3 4
| app.controller.ts app.module.ts app.service.ts main.ts
|
- 入口文件
1 2 3 4 5 6 7 8
| import { NestFactory } from '@nest/core'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(3000); } bootstrap();
|
- 运行应用程序
控制器(Controller)
路由 + 请求(Request)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import { Controller, Get } from '@nestjs/common'; import { Request } from 'express'; @Controller('/api') export class CatsController { @Get('/user-list') findAll(@Req() request: Request): string { return 'This action returns all users'; } }
|
请求方法
GET
POST
PUT
DELETE
PATCH
ALL
OPTIONS
HEAD
Controller常用装饰器
1 2 3 4 5 6 7 8
| @Request @Response @Next @Session @Param @Body @Query @Headers @Ip @HostParam
@HttpCode @Put @Delete @Patch @Options @Head @All
|
- 定义DTO
1 2 3 4 5
| export class CreateCatDto { readonly name: string; readonly age: number; readonly breed: string; }
|
完整示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| import { Controller, Get, Query, Post, Body, Put, Param, Delete } from '@nestjs/common'; import { CreateCatDto, UpdateCatDto, ListAllEntities } from './dto'; @Controller('cats') export class CatsController { @Post() create(@Body() createCatDto: CreateCatDto) { return 'This action adds a new cat'; } @Get() findAll(@Query() query: ListAllEntities) { return `This action returns all cats (limit: ${query.limit} items)`; } @Get(':id') findOne(@Param('id') id: string) { return `This action returns a #${id} cat`; } @Put(':id') update(@Param('id') id: string, @Body() updateCatDto: UpdateCatDto) { return `This action updates a #${id} cat`; } @Delete(':id') remove(@Param('id') id: string) { return `This action removes a #${id} cat`; } }
|
类库
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import { Controller, Get, Post, Res, HttpStatus } from '@nestjs/common'; import { Response } from 'express'; @Controller('cats') export class CatsController { @Post() create(@Res() res: Response) { res.status(HttpStatus.CREATED).send(); } @Get() findAll(@Res() res: Response) { res.status(HttpStatus.OK).json([]); } }
|
提供者(Provider)+ Service
- 服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import { Injectable } from '@nestjs/common'; import { Cat } from './interfaces/cat.interface';
@Injectable() export class CatsService<T> { constructor(private catsService: CatsService) {} @Inject('HTTP_OPTIONS') private readonly httpClient: T; private readonly cats: Cat[] = []; create(cat: Cat) { this.cats.push(cat); } findAll(): Cat[] { return this.cats; } }
|
- 可选提供者
1 2 3 4 5 6 7
| import { Injectable, Optional, Inject } from '@nestjs/common'; @Injectable() export class HttpService<T> { constructor( @Optional() @Inject('HTTP_OPTIONS') private readonly httpClient: T ) {} }
|
模块(Module)
组织应用结构
1 2 3 4 5 6
| @module({ controllers: [CatsController], providers:[CatsService], imports: [], exports: [] })
|
命令行
1 2 3
| $ nest g module cats $ nest g controller cats $ nest g service cats
|
模块
1
| @Module @DynamicModule @Controller @Service @Global
|
动态模块???
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import { Module, DynamicModule } from '@nestjs/common'; import { CreateDatabaseProviders } from './database.providers'; import { Connection } from './connection.provider'; @Module({ providers: [Connection], }) export class DatabaseModule { static forRoot(entities=[], options?): DynamicModule { const providers = CreateDatabaseProviders(options, entities); return { module: DatabaseModule, providers: providers, exports: providers } } }
import { Module } from '@nestjs/common'; import { DatabaseModule } from './database/database.module'; import { User } from './users/entities/user.entity'; @Module({ imports: [DatabaseModule.forRoot([User])], exports: [DatabaseModule], }) export class AppModule {}
|
中间件
主要执行以下任务:
- 执行任何代码
- 对请求和响应对象进行更改
- 结束请求-响应周期
- 调用堆栈中的下一个中间件函数
- 如果当前的中间件函数没有结束请求-响应周期, 它必须调用 next() 将控制传递给下一个中间件函数。否则, 请求将被挂起
- 可以通过constructor注入依赖
定义中间件
1 2 3 4 5 6 7 8 9 10
| import { Injectable, NestMiddleware } from '@nestjs/common'; import { Request, Response, NextFunction } from 'express'; @Injectable() export class LoggerMiddleware implements NestMiddleware { use(req: Request, res: Response, next: NextFunction) { console.log('Request...'); next(); } }
|
- 应用中间件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common'; import { LoggerMiddleware } from './common/middleware/logger.middleware'; import { CatsModule } from './cats/cats.module'; @Module({ imports: [CatsModule], }) export class AppModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer .apply(LoggerMiddleware, cors(), cors1()) .forRoutes({ path: 'cats', method: RequestMethod.GET }); } }
|
- 全局中间件
1 2 3
| const app = await NestFactory.create(AppModule); app.use(logger); await app.listen(3000);
|
异常过滤器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus, } from '@nestjs/common'; @Catch() export class AllExceptionsFilter implements ExceptionFilter { catch(exception: unknown, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse(); const request = ctx.getRequest(); const status = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR; response.status(status).json({ statusCode: status, timestamp: new Date().toISOString(), path: request.url, }); } }
|
管道
管道主要有两个类型:
转换:管道将输入数据转换为所需的数据输出
验证:对输入数据进行验证,如果验证成功继续传递; 验证失败则抛出异常;
类验证器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common'; import { validate } from 'class-validator'; import { plainToClass } from 'class-transformer'; @Injectable() export class ValidationPipe implements PipeTransform<any> { async transform(value: any, { metatype }: ArgumentMetadata) { if (!metatype || !this.toValidate(metatype)) { return value; } const object = plainToClass(metatype, value); const errors = await validate(object); if (errors.length > 0) { throw new BadRequestException('Validation failed'); } return value; } private toValidate(metatype: Function): boolean { const types: Function[] = [String, Boolean, Number, Array, Object]; return !types.includes(metatype); } }
|
全局管道
1 2 3 4 5 6 7 8 9 10 11
| import { Module } from '@nestjs/common'; import { APP_PIPE } from '@nestjs/core'; @Module({ providers: [ { provide: APP_PIPE, useClass: ValidationPipe } ] }) export class AppModule {}
|
转换管道
1 2 3 4 5 6 7 8 9 10 11
| import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common'; @Injectable() export class ParseIntPipe implements PipeTransform<string, number> { transform(value: string, metadata: ArgumentMetadata): number { const val = parseInt(value, 10); if (isNaN(val)) { throw new BadRequestException('Validation failed'); } return val; } }
|
守卫
├── 授权守卫
├── 基于角色认证
├── 绑定守卫
拦截器
├── 绑定拦截器
├── 响应映射
├── 异常映射
nest流程图

控制器
配置请求路由(Get/Post)
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Controller('app') export class AppController { @Get('/list') list(): string { return '我是get请求'; } @Post('/create') create(): string { return '我是post请求'; } }
|
vscode调试node
(1)配置路径: 运行=>添加配置

(2) 选择‘nodejs’

(3) 调试成功

步骤
(1)配置路径: 运行=>添加配置
(2) 选择‘nodejs’
(3) 调试成功
获取请求参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| @Controller('app') export class AppController { @Get('/all') all(@Req() req: Request): string { console.log(req); return 'hello all'; } @Get('/param/:id') getParams(@Param('id') id: number): string { console.log(id); return `获得参数id为${id}`; } @Post('body') getBody(@Body() req: object): object { console.log(req); return req; } @Get('query') getQuery(@Query('name') name: string): string { console.log(name); return `获得参数name为${name}`; } @Get('header') getHeader(@Headers() req: object): object { console.log(req); return req; } }
|
设置响应数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @Controller('app') export class AppController { @Get('/httpCode') @HttpCode(304) getHttpCode() { return 'hello getHttpCode'; }
@Get('/header') @Header('abc', 'xxx') getHeaders() { return 'hello getHeaders'; }
@Get('/redirect') @Redirect('https://nestjs.com', 301) getRedirect(): string { return 'hello getRedirect'; } }
|
调用服务方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| --------------------app.controller.ts----------------------
@Controller('app') export class AppController { constructor(private readonly appservice: AppService) {} @Get('hello') getHello(): string { return this.appservice.getHello(); } }
----------------------app.service.ts------------------------- @Injectable() export class AppService { getHello(): string { return 'Hello World!'; } }
|
共享模块(sharedModule)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| --------------------cats.module.ts----------------- @Module({ controllers: [CatsController], providers: [CatsService], exports: [CatsService], }) export class CatsModule {}
--------------------app.module.ts----------------- @Module({ imports: [CatsModule], controllers: [AppController], providers: [AppService], }) export class AppModule {}
--------------------app.controller.ts----------------- @Controller('app') export class AppController { constructor(private readonly catsService: CatsService) {} @Get('hello') getHello(): string { return this.catsService.getCats(); } }
--------------------common.module.ts----------------- @Global() @Module({ providers: [CommonService], controllers: [CommonController], exports: [CommonService], }) export class CommonModule {}
|
中间件(midddleware)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| --------------------logger.middleware.ts-----------------
export function LoggerMiddleware(req, res, next) { console.log(`Global Logger Request...`); next(); }
@Injectable() export class Logger1Middleware implements NestMiddleware { use(req: Request, res: Response, next: NextFunction) { console.log('Logger1 Request...'); next(); } }
--------------------main.ts---------------------- async function bootstrap() { const app = await NestFactory.create(AppModule); app.use(LoggerMiddleware); await app.listen(3000); } bootstrap();
--------------------app.module.ts----------------- export class AppModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer .apply(Logger1Middleware, Logger2Middleware, Logger3Middleware) .forRoutes('/cats/global-fn-middleware'); } }
|
异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| --------------------app.controller.ts----------------- @Controller('app') export class AppController { constructor(private readonly catsService: CatsService) {} @Get('custom-forbidden') async customForbindden() { throw new ForbiddenException(); } }
--------------------forbidden.exception.ts----------------- export class ForbiddenException extends HttpException { constructor() { super('custom Forbidden', HttpStatus.FORBIDDEN); } }
--------------------main.ts----------------- async function bootstrap() { const app = await NestFactory.create(AppModule); app.use(LoggerMiddleware); app.useGlobalFilters(new AllExceptionsFilter()); await app.listen(3000); } bootstrap();
--------------------any-exception.filter.ts----------------- @Catch() export class AllExceptionsFilter implements ExceptionFilter { catch(exception: unknown, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse(); const request = ctx.getRequest(); const status = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR; console.log('AllExceptionsFilter'); response.status(status).json({ statusCode: status, timestamp: new Date().toISOString(), path: request.url, }); } }
|
管道(pipe)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| --------------------any-exception.filter.ts----------------- const Joi = require('joi'); const schema = Joi.object({ username: Joi.string() .min(3) .max(16) .required(), password: Joi.string() })
@Controller() export class AppController { constructor(private readonly catsService: CatsService) { } @Get('pipe/:id') findOne( @Param('id', new ParseIntPipe()) id: number, ): string { return `get id ${id} `; } @Post('create') @UsePipes(new JoiValidationPipe(schema)) create(@Body() Body) { return '表单创建成功!' } }
--------------------parse-int.pipe.ts----------------- @Injectable() export class ParseIntPipe implements PipeTransform<string> { async transform(value: string, metadata: ArgumentMetadata) { const val = parseInt(value, 10); if (isNaN(val)) { throw new BadRequestException('无效的值'); } return val; } }
--------------------joi.validation.pipe.ts----------------- @Injectable() export class JoiValidationPipe implements PipeTransform { constructor(private schema: Schema) {} transform(value: any, metadata: ArgumentMetadata) { console.log("JoiValidationPipe", value) const { error } = this.schema.validate(value); if (error) { throw new BadRequestException('表单数据填写有误,请重新提交'); } return value; } }
|
守卫(guard)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| --------------------app.controller.ts----------------- @UseGuards(RolesGuard)
@Controller() export class AppController { constructor(private readonly catsService: CatsService) {} @Post('/guards/create') guards() { return 'get guards'; } }
--------------------roles.guard.ts----------------- @Injectable() export class RolesGuard implements CanActivate { canActivate( context: ExecutionContext, ): boolean | Promise<boolean> | Observable<boolean> { const request = context.switchToHttp().getRequest(); console.log('RolesGuard_', request.body); return true; } }
|
拦截器(interceptor)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
| --------------------app.controller.ts----------------- @UseInterceptors(Logging1Interceptor) @Controller() export class AppController { constructor(private readonly catsService: CatsService) {} @Get('interceptor-exception') @UseInterceptors(new ErrorsInterceptor()) exception(): string { throw new HttpException('Forbidden', HttpStatus.FORBIDDEN); return 'get interceptor-exception'; }
@Post('interceptor-response') @UseInterceptors(new TransformInterceptor()) response(): object { return { errCode: -10000, msg: '购买失败', }; } @Get('interceptor') getInterceptor(): string { return 'get Interceptor'; } }
--------------------logging1.interceptor.ts----------------- @Injectable() export class Logging1Interceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable<any> { console.log('Before Logging-1-Interceptor...'); const now = Date.now(); return next .handle() .pipe( tap(() => console.log(`After Logging-1-Interceptor... ${Date.now() - now}ms`)), ); } }
--------------------exception.interceptor.ts----------------- @Injectable() export class ErrorsInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable<any> { console.log('ErrorsInterceptor__'); return next .handle() .pipe(catchError((err) => throwError(new BadGatewayException()))); } }
--------------------transform.interceptor.ts----------------- @Injectable() export class TransformInterceptor<T> implements NestInterceptor<T, Response<T>> { intercept( context: ExecutionContext, next: CallHandler, ): Observable<Response<T>> { console.log('TransformInterceptor'); return next.handle().pipe( map((data) => { console.log(data); typeof data === 'object' ? (data['detail'] = '库存不足') : ''; return data; }), ); } }
async function bootstrap() { const app = await NestFactory.create(AppModule);
app.useGlobalInterceptors(new TransformInterceptor());
await app.listen(3000); } bootstrap();
|
nestjs核心模块