NestJS打包部署的教程
前言
网上关于 NestJS 打包部署的教程很少,然而自己摸索下,分享总结下NestJS 打包部署的经验。
坑点
-
如果执行
nest build命令打包出来的 dist 文件不能直接使用,虽然是打包了,但是你如果将 dist 文件夹移动到其他目录,然后使用node main.js你会发现它不并不能直接运行。 -
prisma 仅能读取本地的 .env 文件,不能根据环境自动切换环境变量文件。数据库连接密码切记要避免特殊字符例如:
$之类的哦,这一点是巨坑。
一般新手部署方式
- 将本地代码先切换到生成环境
- 连接到服务器
- 将本地代码传到服务器
- 执行生成 prisma 客户端
- 执行打包build命令打包 nest
- 使用 pm2 启动服务
痛点
且不说上面几步操作复杂时间长,等部署完成母猪都会上树了。伴随着执行的命令和流程多,人的操作就会容易出错,在刚部署的时候,我也是经常会弄错,部署的时候出现各种各样的错误,这是无法避免的,今天我们将使用1~2条命令完成 nestjs 的部署工作。
部署
针对以上两个问题,在部署 nest 服务会非常繁琐,光 prisma 的部署命令就一大堆,还要区分不同的环境,我们期望的是在部署的时候只要执行一条命令即可完成部署。
解决在 package.json 环境变量文件
先行安装两个包,这两个包将会为我们解决环境变量问题:
pnpm i dotenv
pnpm i dotenv-cli -D
在开发环境默认的启动命令是这样的 nest start --watch,这样命令包括后面所有的命令在读取环境变量文件的时候,默认都是会读取 .env 文件,这并不是我们想要的,我想要不同的环境写在不同的环境变量文件中去。在我执行不同命令的读取不同的环境变量文件。所以我们要做的时候,在项目的根目录建立不同的环境变量文件。
像我自己的项目比较简单我只创建的两个文件,一个是测试环境变量文件,第二个是正式环境变量文件:
.env.development- 开发环境.env.production- 生产环境
创建这个环境变量文件之后,接下来是如何读取这个文件,让它工作起来,这边其实分为两种,1.在package.json 中 2.在代码中如何使用,这里我们先讲第一种。
将原先的默认官方启动命令 nest start --watch 改掉:
"scripts": {
"dev": "dotenv -e .env.development nest start --watch",
}
这时候在执行 dev 命令的时候,就会加载 .env.development 文件。
添加 Prisma 命令
上面讲了nestjs 还要添加 prisma 的命令,这边我就把自己命令贴出来,大家参考下。
下载库 npm-run-all2 可以将多个命令合并成一条命令,后期我们可以将多个命令组合起来使用:
pnpm i npm-run-all2 -D
"scripts": {
"prisma:generate:pro": "dotenv -e .env.production prisma generate",
"prisma:migrate:dev": "dotenv -e .env.development prisma migrate dev",
"build:pro": "run-s prisma:generate:pro build ",
}
prisma:generate:pro- 生产环境中生成 prisma 客户端,在部署生产环境的时候prisma 只要执行这条命令即可prisma:migrate:dev- 测试环境 生成迁移文件 并且 生成 prisma 客户端,在测试和开发中如果更改的表结构经常要执行build:pro- 这就是部署的要执行的最终命令
解决代码中的环境变量问题
这边注意噢上面我们配置的都是在打包的时候环境变量问题,但是代码中读取的环境变量问题一直没有解决,这两个是分开的。那你可能会想,打包环境变量文件解决了,我们用打完的包的代码,不就是已经解决环境变量的问题嘛!这点我一开始也是这么觉得,但是现实就是打包出来的代码和 packages.json 中的环境变量没有任何关系,nestjs 打包是个很奇怪的事情这里就是坑点1说的。
在项目的入口文件也就是 main.ts 中最上面写入指定环境变量代码,我是将这段代码单独抽到一个独立文件中然后引用它,这样不会显很乱:
// config/env.config.ts
import { config } from 'dotenv';
import { resolve } from 'path';
// 根据环境变量加载不同的配置文件
const env = process.env.NODE_ENV || 'development';
const envFile = `.env.${env}`;
config({ path: resolve(process.cwd(), envFile) });
// main.ts
import './config/env.config';
// ... 其他代码
OK 这样我们环境环境变量问题以及打包命令繁琐的事情都已经解决了在这我们对照命令总结下:
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"dev": "dotenv -e .env.development nest start --watch",
"prod": "dotenv -e .env.production nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "cross-env NODE_ENV=production node dist/main",
"prisma:generate:pro": "dotenv -e .env.production prisma generate",
"prisma:migrate:dev": "dotenv -e .env.development prisma migrate dev",
"build:pro": "run-s prisma:generate:pro build "
}
- 开发的时候只需要执行
dev - 部署的时候只需要执行
build:pro
让服务跑起来
我一般会使用将文件上传到服务器上,这样每次我都要将本地的node_modules 删除打包再上传,开发的时候又要下回来,我觉得很烦。
所以我在服务器上安装的 git 执行在服务器上使用 git pull 更新代码即可。
那我们先 git pull 下拉去代码,执行 build:pro 然后使用 pm2 启动下就好啦。
一般 nodejs 服务都是用 pm2 来启动,PM2 是一个守护进程管理工具,帮助您管理和守护您的应用程序。程序报错、死机自动重启,程序日志都比较完善。所有我们一般都会用它来启动我们的 node 服务。在项目根目录添加了一个文件 pm2.config.js:
module.exports = {
apps: [{
name: 'nestjs-app',
script: 'dist/main.js',
instances: 1,
autorestart: true,
watch: false,
max_memory_restart: '1G',
env: {
NODE_ENV: 'production'
}
}]
};
启动命令:
pm2 start pm2.config.js
总结
通过以上配置,我们成功解决了NestJS部署中的两个主要问题:
- 环境变量问题:使用
dotenv-cli和不同环境的配置文件,解决了环境变量切换问题 - 部署流程复杂:通过
npm-run-all2将多个命令合并,实现一键部署
现在部署NestJS应用只需要:
- 开发时:
npm run dev - 部署时:
npm run build:pro+pm2 start pm2.config.js
这样就大大简化了部署流程,提高了开发效率。