NestJS
Nest提供了一个开箱即用的应用程序架构,允许开发人员和团队创建高度可测试、可扩展、松散耦合且易于维护的应用程序。
应用场景
- 从技术层面上,复杂项目(长期+多子系统)推荐使用
- 从人力资源层面上,如果中级前端的人数>后端人数可以考虑
- 从使用成本上说,长期非常香
优缺点
- 代码架构合理,装饰器语法写起来舒服,但是概念比较多
- TS原生支持,开发体验好,项目的代码质量高
- 对于代码人员的要求更高,上手有难度(相比Egg.js/Express/koa)
与其他Node框架相比
- Koa & Express本身只实现了HTTP服务,中间逻辑需自己实现
- Egg.js有合理的逻辑分层,但TS支持不好,文档欠缺
- Nest.js生态最好,使用TS+注解的方式更便捷
资源合集
- 官方示例:https://github.com/nestjs/nest/tree/master/sample
- Awesome:https://github.com/nestjs/awesome-nestjs
- nestjs中文网:https://docs.nestjs.cn/9/introduction
编程思想
FP
FP(Functional Programming)函数式编程,它是确定的数据输入、输出,没有副作用,相对独立。它引用透明,对IDE友好,易于理解。
OOP
OOP(Object Oriented Programming)面向对象式编程,它是抽象现实生活中事物的特征,对于理解友好。它具有封装性(高内聚、低耦合)、继承性、多态性。
AOP
AOP(Aspect Oriented Programming)面向切面编程,能在不破坏封装功能的前提下,额外增加功能。
它的特点有:
- 扩展功能方便,不影响业务之间的逻辑
- 逻辑集中管理
- 更利于代码复用
nestjs中的管道、过滤器、中间件等都是面向切面编程的实现。
IOC和DI
IOC(Inversion Of Control)控制反转是一种面向对象编程中的一种设计原则,用来减低计算机代码之间的耦合度。其基本思想是:借助于第三方实现具有依赖关系的对象之间的解耦。
DI(Dependency Injection)依赖注入是一种用于实现IOC的设计模式,它允许在类外创建依赖对象,并通过不同的方式将这些对象提供给类。
核心概念
- Controllers(控制器):负责处理请求、返回响应
- Services(服务):负责提供方法和操作,只包含业务逻辑
- Modules(模块):组合所有的逻辑代码
- Pipes(管道):核验请求的数据
- Filters(过滤器):处理请求时的错误
- Guards(守卫):鉴权与认证相关
- Interceptors(拦截器):给请求与响应加入额外的逻辑
- Repositories(存储库):负责访问数据库中的数据
Nestjs中使用模块Module来组织应用程序,使用@Module装饰器来描述模块。模块中有4大属性:imports、providers、controllers、exports。
Nestjs将模块分为四大块:功能模块、共享模块、全局模块、动态模块。功能模块与共享模块是一回事,只是叫法不一样。全局模块通常应用在配置、数据库连接、日志上。动态模块是在使用到模块的时候才初始化。
MVC
Nestjs可以通过模板库实现View层,常见:pug、hus、ejs等,Nestjs默认集成express作为web服务器,可以换成koa/fastify。Controller响应前端的请求,Model是对应的具体的数据库逻辑。
DTO(Data Transfer Object)数据传输对象,例如前端请求到后端这是一个传输的过程,后端访问数据库又是一个传输的过程。Nestjs中的DTO约定了数据字段、属性,方便数据校验。
DAO(Data Access Object)数据访问对象,它是一层逻辑:包含实体类、数据库操作(CURD)、数据校验、错误处理等,Nestjs做了一层更高级的封装,通过ORM库与各种类型数据库对接,而这些ORM库就是DAO层。
生命周期
接口服务
官方CLI
官方的CLI是一个命令行界面工具,以帮助您初始化、开发和维护Nest应用程序。它以多种方式提供帮助,包括搭建项目、以开发模式为其提供服务,以及为生产分发构建和打包应用程序。它体现了最佳实践的架构模式,以构建良好的应用程序。
安装
首先全局安装nest cli脚手架,然后通过脚手架来创建项目
npm i -g @nestjs/cli
nest new project-name
工程目录
为了去理解Python的语言设计之美,其实更要理解这样的一句话"约定大于配置",好的工程化目录(约定)能够很好的提升项目的可维护性。
作者推荐
在官方的issues中,我们可以找到一些提示:Best scalable project structure这里有作者的回复。
- src
- core
- common
- middleware
- interceptors
- guards
- user
- interceptors (scoped interceptors)
- user.controller.ts
- user.model.ts
- store
- store.controller.ts
- store.model.ts
- 可以使用monorepo的方法一一在一个repo中创建两个项目,并在他们之间共享共同的东西,如库/包。
- 没有模块目录,按照功能进行划分。
- 把通用/核心的东西归为单独的目录:common,比如:拦截器/守卫/管道
参考项目
技术栈:Nest + sequelize-typescript + JWT + jest + Swagger
特点:
- 项目文档及相关的资源在根目录
- 数据库及项目配置会放在根目录
- src中会对功能进行划分,建不同的文件夹users、posts
- 单个功能文件夹中,会包括一个完整的CURD的相关文件(dto/controller/module/providers/service)
- 抽离公共配置到shared文件夹
特点:
- 项目文档及相关的资源在根目录
- src中modules会对功能进行划分建不同的文件夹
- 单个功能文件夹中,会包括一个完整的CURD的相关文件(dto/controller/module/providers/service)
- 把公共的代码(按照nestjs逻辑分层)拆成单独的文件夹guards、filters、decorators、interceptors、interfaces、errors
特点:
- 项目文档及相关的资源在根目录,包括typings、test、bin
- src中会对功能进行划分建不同的文件夹
- 抽离公共代码到common文件夹,配置文件放在config文件件,实体类放置在entity中
- 鉴权相关的逻辑放在auth
- 把同类的guards、filters、decorators、interceptors、interfaces、errors存放在common文件夹中
核心部分
通过后端框架核心部分主要包括开发层面、功能层面、接口安全层面思考要做哪些内容。
例如从开发层面我们要考虑多环境配置,功能层面考虑通用模块:用户、菜单、权限、日志,最后是接口文档、接口请求安全&性能等。
多环境配置
官方提供了@nestjs/config模块:
pnpm i --save @nestjs/config
然后配置src/app.module.ts
import { Module } from '@nestjs/common';
import { UserModule } from './user/user.module';
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
}),
UserModule,
],
controllers: [],
providers: [],
})
export class AppModule {}
然后在项目根目录下创建.env文件,编写环境配置。然后在Controller中进行使用。
import { Controller, Get } from '@nestjs/common';
import { UserService } from './user.service';
import { ConfigService } from '@nestjs/config';
@Controller('user')
export class UserController {
constructor(
private readonly userService: UserService,
private readonly configService: ConfigService,
) {}
@Get()
public getUser() {
const db = this.configService.get('DB');
console.log(db);
return this.userService.getUser();
}
}
如果我们想使用yaml配置的话,安装js-yaml和@types/js-yaml。
配置验证
配置验证,主要是指在应用程序启动时,如果没有提供所需的环境变量或不符合某些验证规则,就会抛出一个异常。@nestjs/config
包实现了两种不同的方式来实现这一点。
- Joi内置验证器。通过Joi,你可以定义一个对象模式,并根据它验证JavaScript对象
- 一个自定义的validate()函数,它将环境变量作为输入
Joi用法
最新版本的joi需要你运行Node v12或更高版本,旧版本的node请安装v16.1.8.这主要是因为在v17.0.2发布后,在构建的时候会出现错误。joi最好配合官方的@nestjs/config进行使用。
安装依赖
npm install --save joi
定义验证schema
import { Module } from '@nestjs/common';
import { UserModule } from './user/user.module';
import { ConfigModule } from '@nestjs/config';
import configuration from './configuration';
import * as joi from 'joi';
// const envFilePath = `.env.${process.env.NODE_ENV || 'development'}`;
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
//envFilePath,
load: [configuration],
validationSchema:joi.object({
DB_PORT: joi.number.default(3306)
})
}),
UserModule,
],
controllers: [],
providers: [],
})
export class AppModule {}
ORM
ORM(Object Relational Mapping)对象关系映射,其主要作用是在编程中,把面向对象的概念跟数据库中的概念对应起来。
例如:定义一个对象,那就对应着一张表,这个对象的实例,就对应着表中的一条记录。
ORM特点:
- 方便维护:数据模型定义在同一个地方,利于重构
- 代码量少、对接多种库:代码逻辑更易懂
- 工具多,自动化能力强:数据库删除关联数据、事务操作等
在nestjs中官方提供了一个非常好用的ORM模块叫TypeORM。具体的可以查看官方文档https://typeorm.io/,中文文档地址为https://typeorm.bootcss.com/。
npm install --save @nestjs/typeorm typeorm mysql2
安装过程完成后,我们可以将其导入TypeOrmModule到根目录中AppModule
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'root',
database: 'test',
entities: [],
synchronize: true,
}),
],
})
export class AppModule {}
日志
日志等级主要分为以下几种:
- Log:通用日志,按需进行记录(打印)
- Warning:警告日志,比如:尝试多次进行数据库操作
- Error:严重日志,比如:数据库异常
- Debug:调试日志,比如:加载数据日志
- Verbose:详细日志,所有的操作与详细信息(非必要不打印)
按照功能分为以下几类:
- 错误日志:方便定位问题,给用户友好的提示
- 调试日志:方便开发
- 请求日志:记录敏感行为
日志记录位置:
- 控制台日志:在开发时方便查看
- 文件日志:方便回溯与追踪(24小时滚动)
- 数据库日志:敏感操作、敏感数据记录