2023-05-11-【工程化】初始nestjs入门开发&部署

一、前言背景

1.1 什么是 nestjs

NestJS 是一个用于构建高效、可扩展的 Node.js 服务器端应用程序的开发框架。它利用 JavaScript 的渐进增强的能力,结合了 OOP(面向对象编程)、FP(函数式编程)和 FRP(函数响应式编程)等编程范式。NestJS 建立在强大的 HTTP 服务器框架上,例如 Express,并提供了更高层次的抽象,同时仍然向开发者直接暴露了底层框架的 API。

1.2 什么是 nodejs

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。它使得开发者能够高效地在服务器端运行 JavaScript 代码。Node.js 提供了一个非阻塞 I/O 模型,使得开发者能够轻松地编写服务器端应用程序。Node.js 还提供了一系列丰富的模块,使得开发者能够轻松地扩展应用程序的功能。

1.3 javascript 运行环境

安装yapi

1.4 前端框架比较

框架 特点 缺点 适用场景
NestJS 高效;轻量级;基于装饰器;依赖注入;管道;中间件;拦截器等机制 学习成本较高;对开发者编程素养和面向对象开发思想的要求较高 中大型企业级 Web 应用;需要高效、可扩展的框架
Next.js 基于 React 和 React Router;服务器端渲染;生态圈繁荣 性能相对较低;需要使用 React 的知识 中大型企业级 Web 应用;需要高效、可扩展的框架
Nuxt.js 基于 Vue.js 的轻量级应用框架;服务器端渲染;生态圈繁荣 性能相对较低;需要使用 Vue 的知识 中大型企业级 Web 应用;需要高效、可扩展的框架
Egg.js Egg.js 的企业级规范和强大的功能;多线程支持;类型检查良好;基于 Koa2 开发 学习成本较高;对开发者编程素养和面向对象开发思想的要求较高 中大型企业级 Web 应用;需要高效、可扩展的框架
Koa2 轻量级;异步中间件模型;生成器;上下文对象 学习曲线较陡峭;对异步编程的认知要求较高 中小型企业级 Web 应用;需要高效、可扩展的框架
Express 广泛的使用和生态圈;中间件模型;简单易学;主要用于构建 RESTful API 服务器 大而全,不够轻量级;性能相对较低 小型企业和个人项目;对性能要求不高的应用场景

1.5 nestjs 与 spring boot 对比

nestjs spring boot
开发语言 TypeScript 或 JavaScript Java 或 Kotlin
发布时间 2017 2014
学习曲线 易上手 成本高
扩展性
文档和社区 较新,不全面 完善且丰富
维护成本 人力成本高 适中
轻量级
处理 I/O 任务 高效 一般
单线程
内存使用率 一般
多线程支持
执行计算量大的任务 一般 高效
严格的类型检测 一般
测试工具 jest Junit
应用场景 轻量级高性能中大型应用 大型复杂应用

二、nestjs 基本概念及用法

Nest 的目标是成为一个与平台无关的框架,目前支持两个框架:Express 和 Fastify,默认使用 Express。NestJS 对于这两个框架进行了一定的封装,也支持直接使用它们的 API,因此给开发者带来了很大的自由度。

NestJS 提供了一个开箱即用的框架,能够创建可测试、可扩展、低耦合和易于维护的应用程序。它的设计思想受到 Angular 的启发,同时也借鉴了其他 MVC 框架的概念和最佳实践。可使用 nest-cli 脚手架创建新的 NestJS 项目。

2.1 主要组件

2.1.1 控制器 Controller

  • 处理传入的请求并向客户端返回响应
  • 被 @Controller 装饰的类 就是 一个 Controller
  • 参数获取 @Body(), @Query(), @Res(); 返回 @Res()
  • nest g resource test

Controllers

2.1.2 提供者 Providers

  • 提供者是 Nest 中的一个基本概念。许多基本的 Nest 类都可以被视为提供者——服务、存储库、工厂、助手等。提供者的主要思想是它可以注入依赖项;这意味着对象之间可以创建各种关系,并且“连接”对象实例的功能可以在很大程度上委托给 Nest 运行时系统。
  • 被 @Injectable 装饰的类 都是 Providers
  • 创建服务 nest g service cats
    Controllers

2.1.3 模块 Modules

  • 模块是用@Module()装饰器注释的类。装饰@Module()器提供 Nest 用来组织应用程序结构的元数据。
  • 每个应用程序至少有一个模块 - 根模块。根模块是 Nest 用来构建应用程序图的起点。

Controllers

2.1.4 中间件 Middleware

  • 中间件是在路由处理程序之前调用的函数,可以访问请求和响应对象。
  • 在函数中或在带有@Injectable()装饰器的类中实现自定义 Nest 中间件。类应该实现 NestMiddleware 接口,而函数没有任何特殊要求
1
2
@Injectable()
export class LoggerMiddleware implements NestMiddleware {

Controllers

2.1.5 管道 Pipes

  • 管道是用@Injectable()装饰器注释的类。管道应该实现 PipeTransform 接口。
  • 用途:1. 转换:将输入数据转换为所需的形式;2. 校验:评估输入数据,如果有效,则简单地通过它;否则,当数据不正确时抛出异常
  • 管道都在 arguments 由控制器路由处理程序处理的对象上运行。
  • 示例
1
2
3
4
@Get(':id')
async findOne(@Param('id', ParseIntPipe) id: number) {
return this.catsService.findOne(id);
}

2.1.6 守卫 Guards

  • 守卫是用@Injectable()装饰器注释的类。实现 CanActivate 接口。
    用途:单一职责授权操作。使用@UseGuards() 绑定。
  • 与管道和异常过滤器一样,守卫可以是控制器范围的、方法范围的或全局范围的。
1
2
@Injectable()
export class AuthGuard implements CanActivate {

Controllers

2.1.7 拦截器

  • 使用@Injectable()装饰器注解的类,实现 NestInterceptor 接口,位于请求和响应之间的一段代码。
  • 用途:在函数执行前/后绑定额外的逻辑,如:权限验证,日志记录,性能监控。
  • 拦截器获取请求信息并仅在发送响应时执行阻塞操作。这样一来,传入的请求就不会因 Node.js 进程繁忙而受到限制,并且应用程序可以在后台进行异步处理(与 i/o 相关)。

Controllers

2.2 请求生命周期

  • 收到请求

  • 中间件

    1. 全局绑定的中间件
    1. 路径中指定的 Module 绑定的中间件
  • 守卫

    1. 全局守卫
    1. Controller 守卫
    1. Route 守卫
  • 拦截器(Controller 之前)

    1. 全局
    1. Controller 拦截器
    1. Route 拦截器
  • 管道

    1. 全局管道
    1. Controller 管道
    1. Route 管道
    1. Route 参数管道
  • Controller(方法处理器)

  • 服务

  • 拦截器(Controller 之后)

    1. Route 拦截器
    1. Controller 拦截器
    1. 全局拦截器
  • 异常过滤器

    1. Route
    1. 控制器
    1. 全局
  • 服务器响应

  • 注: Route 代表 Controller 下的 method。

2.3 项目搭建

2.4 数据库操作 typeorm

TypeORM 是一个对象关系映射(ORM)库,可用于 Node.js 和各种浏览器中的不同数据库,如 MySQL、PostgreSQL、Oracle、MSSQL 和 SQLite。它支持 Active Record 和 Data Mapper 模式,具有类型安全、易于使用和可扩展的优点。TypeORM 旨在支持最新的 JavaScript 特性,并提供额外的功能以帮助开发人员快速构建健壮的数据库应用程序。

  1. 安装依赖
1
npm install @nestjs/typeorm typeorm mysql
  1. 配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm';
...

@Module({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: 'x.x.x.x',
port: 3306,
username: 'root',
password: 'xxxx',
database: 'xxxx',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true,
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule { }

2.5 构建,区分环境

  1. 安装依赖包
1
npm install @nestjs/config cross-env
  1. 根目录创建 .env.dev 及 .env.prod 文件
  2. 配置
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
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm';
import { ScheduleModule } from '@nestjs/schedule';
import { ConfigModule, ConfigService } from '@nestjs/config';

const envFilePath = `.env.${process.env.NODE_ENV || 'dev'}`;

@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath,
}),
//配置数据库链接
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => {
return {
type: 'mysql',
host: config.get('DATABASE_HOST'),
port: config.get('DATABASE_PORT'),
username: config.get('DATABASE_USER'),
password: config.get('DATABASE_PASSWORD'),
database: config.get('DATABASE_DATABASE'),
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true,
} as TypeOrmModuleOptions;
},
}),
ScheduleModule.forRoot()
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule { }

  1. 修改 package.json 启动及打包指令
1
"start": cross-env NODE_ENV=dev nest start --watch

2.6 定时任务

1
import { ScheduleModule } from '@nestjs/schedule';

Nestjs 开发环境和生产环境的配置

断点调试

https://juejin.cn/post/7035954397012033566

VScode 调试

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
其中 program 字段是关键,开启调试器的时候会从这个入口启动应用。

自动生成的 lanuch.json 配置文件的 program 已经默认有值了,那是因为VSCode 在初始化这个配置文件的时候,会解析项目的 package.json 文件里的 script.start 脚本,并把解析内容填充到 lanuch.jsonprogram 字段。

{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "启动程序",
"skipFiles": [
"<node_internals>/**"
],
// 启动调试器之前运行任务进行编译TS项目
"program": "${workspaceFolder}",
"outFiles": [
"${workspaceFolder}/pay-back/packages/server/dist/**/*.js"
]
}
]
}

三、部署

3.1 PM2 介绍

  1. pm2 是开源的基于 Nodejs 的进程管理器,包括守护进程、监控、日志的一整套完整的功能;
  2. pm2 带有负载均衡功能(使用 Node cluster 集群模块),可以保持 node 应用进程永远运行在后台
  3. 0 秒停机重载,维护升级时不需要停机;
  4. 安装 pm2 pnpm install pm2 -g

3.2 PM2 部署 nestjs 项目

  1. 在项目根目录下创建一个 ecosystem.config.js 文件,配置 PM2:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
module.exports = {
apps: [
{
name: "my-nestjs-project",
script: "dist/main.js",
env: {
NODE_ENV: "development",
},
env_production: {
NODE_ENV: "production",
},
},
],
};
  1. 使用 PM2 启动项目
1
2
3
# 生产环境使用以下命令启动项目:

pm2 start ecosystem.config.js --env production

该命令会根据 ecosystem.config.js 中的配置,以生产环境启动 NestJS 项目。

  • PM2 命令
  1. pm2 start app.js:启动 nodeJs 应用,进程的默认名称为文件名 app
  2. pm2 start app.js –name mynode:启动 node,并指定进程名称为 mynode
  3. pm2 start app.js -i max:根据有效 CPU 数目启动最大进程数目
  4. pm2 start app.js –watch:实时监控的方式启动,app.js 文件有变动时,pm2 会自动 reload
  • 查看与监视进程
  1. pm2 list:显示所有进程;
  2. pm2 show 0,pm2 info 0:查看进程 id 为 0 的详细信息;
  3. pm2 monit:进入监视页面,监视每个 node 进程的 CPU 和内存的使用情况。
  • 停止、删除进程
  1. pm2 stop/delete 0:停止/删除 id 为 0 的进程;
  2. pm2 stop/delete all:停止/删除所有进程。
  • 重启、重载
  1. pm2 restart 0:重启 id 为 0 的进程;
  2. pm2 restart all:重启所有进程;
  3. pm2 reload 0:0 秒停机重载 id 为 0 进程(用于 NETWORKED 进程);
  4. pm2 reload all:重载所有进程。
  • 日志操作
  1. pm2 logs:显示所有进程的日志;
  2. pm2 logs 0:显示进程 id 为 0 的日志;
  3. pm2 flush:清空所有日志文件;
  4. pm2 reloadLogs:重载所有日志。
  5. pm2 startup:产生 init 脚本,保持进程活着。

2. docker 容器化部署

其他

mysql 自动备份
https://blog.51cto.com/u_15906694/6007052

  1. 宿主机创建 backUp 文件夹
  2. 执行备份指令
1
docker exec -it mysql(容器名)  /bin/bash -c 'mysqldump -uroot -pjueduianquan996 --databases 需要备份的数据库' > /usr/local/mysql/backup/user_`date +%F`.sql(宿主机的文件路径);
1
docker exec -i mysql /bin/bash -c 'mysqldump -uroot -pjueduianquan996 --databases blowsysun' > /usr/local/mysql/backup/funds_data_`date +\%F`.sql
  1. 创建定时任务
  • linux 上最常用的计划任务软件叫 crontab,该软件的命令同时也叫 crontab。
1
2
3
4
# 常用指令
crontab -l:查看当前定时任务
crontab -e:编辑、新建定时任务
0 0 19 * * 1-5 /usr/local/mysql/backup/timerTask.sh
  1. 恢复数据
1
2
3
4
5
6
7
8
恢复数据库前,同样需要进入MySQL容器的shell环境,命令与备份时相同:

docker exec -it mysql-container bash
使用mysql命令恢复数据库
在容器内部,我们可以使用mysql命令恢复数据库。例如,从/backup/mydatabase.sql文件恢复名为mydatabase的数据库,可以执行以下命令:

mysql -u root -p mydatabase < /backup/mydatabase.sql
注意:上述命令中的root为用户名,执行该命令时会提示输入密码。
  1. 从服务器下载文件
1
scp root@43.154.209.141:/usr/local/mysql/backup/funds_data_2024-11-08.sql /Users/sunny/Desktop

2023-05-11-【工程化】初始nestjs入门开发&部署
https://zhangyingxuan.github.io/2023-05-11-【工程化】初始nestjs入门开发&部署/
作者
blowsysun
许可协议