NestJS 学习笔记


简介

Nest 是一个用于构建 Node.js 服务器端应用程序的框架。内置 TypeScript(也允许用纯 JavaScript编写代码)并结合了 OOP(面向对象编程),FP(函数式编程)和 FRP(函数式响应编程)的元素及思想,设计灵感来自于Angular。
Angular 的很多模式又来自于 Java 中的 Spring 框架,依赖注入面向切面编程等,所以你可以认为: Nest 是 Node.js 版的 Spring 框架

同类型框架比较

  • Midway
  • Egg
  • Express

起步

相关命令

#安装脚手架
$ npm i -g @nestjs/cli

#使用脚手架创建一个项目
$ nest new [project-name]

#默认启动项目命令
$ npm run start

#打包项目
$ nest run build

脚手架初始化项目 - 选项

- Which package manager would you to use?(选择你想使用的包管理器)
  yarn | npm(默认) | pnpm

初始目录结构

src
 ├── app.controller.spec.ts  对于基本控制器的单元测试样例
 ├── app.controller.ts  带有单个路由的基本控制器示例
 ├── app.module.ts 应用程序的根模块
 ├── app.service.ts 带有单个方法的基本服务提供者
 └── main.ts 应用程序入口文件

其他指令

#查看帮助选项
$ nest --help|-h

#自动在目录中创建一个名为[module-name]的控制器模块文件
$ nest generate|g co [module-name]

#显示项目信息
$ nest info

#更新依赖包
$ nest update|u

#添加依赖包
$ nest add [library-name]
Generate命令说明(点击查看):
名称 别名 说明
application application 在当前目录创建新的应用(同new指令)
class cl 生成一个空class文件
configuration config 生成CLI配置文件
controller co 生成并声明一个控制器模块
decorator d 生成一个自定义装饰器文件
filter f 生成并声明一个过滤器模块
gateway ga 生成并声明一个网关模块
guard gu 生成并声明一个权限守卫模块
interceptor in 生成并声明一个拦截器模块
interface interface 生成并声明一个接口定义模块
middleware mi 生成并声明一个中间件模块
module mo 生成并声明一个模块管理文件
pipe pi 生成并声明一个管道模提供者
resolver r 生成并声明一个GraphQL解析器模块
service s 生成并声明一个服务模块
library lib 在monorepo中生成新库
sub-app app 在monorepo中生成新应用程序
resource res 生成一个完整的CRUD资源目录

定义

Controller(控制器)

通俗来说就是路由Router,负责处理客户端传入的请求参数并向客户端返回响应数据,也可以理解是HTTP请求的逻辑处理。

注:要使用 CLI 创建控制器类,只需执行 $ nest g co [name] 命令。

示例:

/* dto/create-cat.dto.ts */

//定义一个Dto类,规定请求的入参格式。
export class CreateCatDto {
  readonly name: string;
  readonly age: number;
  readonly breed: string;
}
------------------------------------
/* cats.controller.ts */

import { Controller, Get, Query, Post, Body, Put, Param, Delete } from '@nestjs/common';
import { CreateCatDto, UpdateCatDto, ListAllEntities } from './dto';

@Controller('cats') //定义了一个前缀为'cats'的路由模块
export class CatsController {
  @Post() //通过Post方法访问到"/cats/"路由
  create(@Body() createCatDto: CreateCatDto) {
    return '此路由将添加一条新数据';
  }

  @Get() //通过Get方法访问到"/cats/"路由
  findAll(@Query() query: ListAllEntities) {
    return `此路由将返回所有数据 (limit: ${query.limit} items)`;
  }

  @Get(':id') //通过Get方法访问到"/cats/${id}"路由
  findOne(@Param('id') id: string) {
    return `此路由将根据${id}返回指定数据`;
  }

  @Put(':id') //通过Put方法访问到"/cats/${id}"路由
  update(@Param('id') id: string, @Body() updateCatDto: UpdateCatDto) {
    return `此路由将根据${id}更新指定数据`;
  }

  @Delete(':id') //通过Delete方法访问到"/cats/${id}"路由
  remove(@Param('id') id: string) { //delete为js保留关键字,可以使用remove命名
    return `此路由将根据${id}删除指定条目`;
  }
}

Providers(提供者)

Providers 是一个用@Injectable()装饰器注释的类。许多基本的 Nest 类可能被视为 provider,如service, repository, factory, helper等等,通过 constructor(构造器) 注入依赖关系。

注:要使用 CLI 创建服务类,只需执行 $ nest g s [name] 命令。

最常见的是使用@Injectable()装饰器创建一个提供数据操作服务的类,即service模块。
示例:

/* interfaces/cat.interface.ts */

//声明一个接口,规定请求的入参格式。
export interface Cat {
  name: string;
  age: number;
  breed: string;
}
------------------------------
/* cats.service.ts */

import { Injectable } from '@nestjs/common';
import { Cat } from './interfaces/cat.interface';

@Injectable()
export class CatsService {
  private readonly cats: Cat[] = [];

  create(cat: Cat) {
    this.cats.push(cat);
  }

  findAll(): Cat[] {
    return this.cats;
  }
}

然后在CatsController里使用它:

/* dto/create-cat.dto.ts */

export class CreateCatDto {
  readonly name: string;
  readonly age: number;
  readonly breed: string;
}
----------------------------------
/* cats.controller.ts */

import { Controller, Get, Post, Body } from '@nestjs/common';
import { CreateCatDto } from './dto/create-cat.dto';
import { CatsService } from './cats.service';
import { Cat } from './interfaces/cat.interface';

@Controller('cats')
export class CatsController {
  constructor(private catsService: CatsService) {}

  @Post()
  async create(@Body() createCatDto: CreateCatDto) {
    this.catsService.create(createCatDto);
  }

  @Get()
  async findAll(): Promise {
    return this.catsService.findAll();
  }
}

定义了服务模块(提供者)和控制器路由模块(使用者)之后,还需要在模块管理文件中进行注册。
示例:

//app.module.ts
import { Module } from '@nestjs/common';
import { CatsController } from './cats/cats.controller';
import { CatsService } from './cats/cats.service';

@Module({
  controllers: [CatsController],
  providers: [CatsService],
})
export class AppModule {}

然后我们就拥有了现在的目录结构:

src
├── cats
│    ├──dto
│    │   └──create-cat.dto.ts
│    ├── interfaces
│    │       └──cat.interface.ts
│    ├──cats.service.ts
│    └──cats.controller.ts
├──app.module.ts
└──main.ts

Module(模块)

模块是具有 @Module() 装饰器的类。 @Module() 装饰器提供了元数据,Nest 用它来组织应用程序结构。
要使用 CLI 创建模块,只需执行 $ nest g module cats 命令。

每个 Nest 应用程序至少有一个模块,即根模块(app.module)。一般情况下应用程序可以按照功能划分若干个子模块,比如cats模块。
示例:

/* 子模块 cats/cats.module.ts */

import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Module({
  controllers: [CatsController],
  providers: [CatsService],
  exports: [CatsService] //若需要在其它模块中CatsService实例,就需要把它导出去。
})

//Provider也可以注入到模块的导出类中,方便用于其他配置。
export class CatsModule {
  constructor(private readonly catsService: CatsService) {}
  /// 可以通过注入Provider,在这里做一些其它事情...
}
------------------------------------------
/* 根模块 app.module.ts */

import { Module } from '@nestjs/common';
import { CatsModule } from './cats/cats.module';

@Module({
  imports: [CatsModule], //在这里导入子模块
})
export class ApplicationModule {}

按照上述的导出模块以共享实例,可能不能够满足某些特殊情况。比如我们在很多地方中都用到了相同的模块,或者想要一些模块即取即用(比如helper,数据库连接等),如果在每个地方都这样导入导出就太麻烦了。所以可以把这类模块,通过 @Global 装饰器注册到全局。
示例:

import { Module, Global } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Global()
@Module({
  controllers: [CatsController],
  providers: [CatsService],
  exports: [CatsService],
})
export class CatsModule {}

通过按照功能划分模块之后,我们的项目结构现在是这样的:

src
├──cats
│    ├──dto
│    │   └──create-cat.dto.ts
│    ├──interfaces
│    │     └──cat.interface.ts
│    ├─cats.service.ts
│    ├─cats.controller.ts
│    └─cats.module.ts
├──app.module.ts
└──main.ts

Middleware(中间件)

中间件是在路由处理程序(controller)之前调用的函数,使用@Injectable()装饰器定义类。提供三个回调参数,请求对象(request)、响应对象(response)和中间件函数(next())。
注:要使用 CLI 创建中间件函数,只需执行 $ nest g mi [name] 命令。
中间件功能:

  • 执行任何代码。
  • 对请求和响应对象进行更改。
  • 结束请求-响应周期。
  • 调用堆栈中的下一个中间件函数。
  • 如果当前的中间件函数没有结束请求-响应周期, 它必须调用 next() 将控制传递给下一个中间件函数。否则, 请求将被挂起。

示例:

/* logger.middleware.ts */

import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { CatsService } from './cats/cats.service';

//使用类的方式创建中间件
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  //同样支持依赖注入
  constructor(private readonly catsService: CatsService) {}
  use(req: Request, res: Response, next: NextFunction) {
    console.log(this.catsService.findAll());
    next();
  }
}

//使用函数的方式创建中间件
export function logger(req, res, next) {
  console.log(`Request...`);
  next();
};

然后在app.module中挂载(中间件并不能在@Module()装饰器中像controller数组一样列出,必须在实现NestModule接口的类上使用configure方法来设置和挂载)。
示例:

/* app.module.ts */

import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware, logger } 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, logger) //可以指定一个或多个中间件
      .forRoutes('cats'); //通过forRoutes可以将中间件限制在制定路由下面使用。
  }
}

MiddlewareConsumer是专门用来管理中间件的帮助类。forRoutes方法支持传入字符串、对象或控制器类,并支持正则表达式的匹配。并且可以通过前置方法exclude来标记需要排除的路由。
示例:

consumer
  .apply(LoggerMiddleware)
  .exclude(
    { path: 'cats', method: RequestMethod.GET },
    { path: 'cats', method: RequestMethod.POST },
    'cats/(.*)',
  )
  .forRoutes(CatsController);

// forRoutes({ path: 'cats', method: RequestMethod.GET })
// forRoutes({ path: 'ab*cd', method: RequestMethod.ALL });

也可以使用入口文件(main)的app.use()方法,全局挂载中间件到所有路由上:

const app = await NestFactory.create(AppModule);
app.use(logger);
await app.listen(3000);

Filter(过滤器)

过滤器用于处理整个应用程序中所抛出抛出的异常,当捕获到未处理的异常时,我们可以使用过滤器来做一些操作。
注:要使用 CLI 创建中间件函数,只需执行 $ nest g f [name] 命令。

未完待续。。。