开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第9天,点击查看活动详情
最近学习Nestjs的时候做了一个项目,整合了Typescript+Prisma+MySQL,开发时跑的好好的,没有任何问题,后来想打包部署试一试,中途遇到到了很多问题,也在网上查阅了很多资料,我试了很多办法,包括pkg打包(还没有成功)、docker容器部署等,可行的还是有几个,但是一直不是我要的,不过经过不懈的研究努力,我终于还是达到了自己的目的!
前情提要,需要大家使用过prisma,知道怎么在nestjs中集成prisma,默认的集成非常简单,可以查看官网说明。Prisma | NestJS - A progressive Node.js framework
一、坎坷旅途
1. 普通的build不会打包依赖
这里我参阅了掘金另一位大佬的文章,具体里面的细节文章讲的很清楚,想了解学习的可前往juejin.cn/post/706572…
简单来说,使用nest build命令进行打包,默认是不会将依赖打包进去的,需要将整个项目拷贝到服务器上才行,如果环境不一样,可能需要重新安装依赖,在服务器上安装node_modules真是太占用资源了!根据前面大佬的文章,我开始研究了webpack模式打包,按照大佬的步骤一步一步走,
nest build通过webpack-node-externals插件默认屏蔽了依赖的打包,我们手动配置externals为空,让node_modules依赖也参加打包,配置webpack.config.js文件如下(直接拷贝的大佬配置),
/* eslint-disable @typescript-eslint/no-var-requires */
const path = require('path');
const webpack = require('webpack');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
console.log('start build');
module.exports = {
mode: 'production',
entry: './src/main',
target: 'node',
externals: {},
module: {
rules: [
{
test: /.ts?$/,
use: {
loader: 'ts-loader',
options: { transpileOnly: true },
},
exclude: /node_modules/,
},
],
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist'),
},
resolve: {
extensions: ['.js', '.ts', '.json'],
},
plugins: [
new webpack.IgnorePlugin({
checkResource(resource) {
const lazyImports = [
'@nestjs/microservices',
'@nestjs/microservices/microservices-module',
'cache-manager',
'class-validator',
'class-transformer',
];
if (!lazyImports.includes(resource)) {
return false;
}
try {
require.resolve(resource, {
paths: [process.cwd()],
});
} catch (err) {
return true;
}
return false;
},
}),
new ForkTsCheckerWebpackPlugin(),
],
};
修改package.json中的scripts代码,增加webpack打包模式,即下面的"build-wp":
"scripts": {
"build": "nest build",
"build-wp": "nest build --webpack --webpackPath=./webpack.config.js",
},
而后执行
➜ npm run build-wp
当我欣喜等待的时候,报错了,大致信息是Can't resolve 'class-transformer/storage' in......:
经过查阅github的相关issue,找到了解决方案:github.com/nestjs/mapp…,其实也很简单,就是将'class-transformer/storage'也加入到IgnorePlugin的lazyImports中去,这也是无用的依赖包,如果你在打包过程中遇到同样的问题,可以照此执行,将相关依赖放入到lazyImports中:
new webpack.IgnorePlugin({
checkResource(resource) {
const lazyImports = [
//... ...
'class-transformer/storage',
];
......
}),
这样做完后打包成功,没有任何问题。
可以看到打包后的文件3.4M大小,已经包含了依赖
接下来就是运行了
2. 项目目录下直接使用node运行main.js
在项目目录下执行node dist/main.js
可以发现执行完全正常,我觉得大工告成了!
3. 拷贝到其他目录单独执行
不过我还是想简单测试一下,我把dist目录拷贝到了别的地方,直接进入dist目录,执行同样的操作
node main.js
报错又来了!我在写文章的时候,我的webstorm突然崩溃了起不起来,把我气得!换vscode继续撸吧,我想把当时的错误都重现一遍。
可以看出,缺少了prisma.shcema文件,原来还需要这个文件呀!这简单,我直接把该文件拷贝到dist目录中,继续执行!
终端教会我做人!在经历了将近仅10s的一顿乱码操作后,终于爆出了错误信息!
赶紧上网查了一下,原来prisma在生成client的时候是需要指定平台的,比如报错中就是我没有指定"darwin",不过还好报错信息比较友好,一眼就知道大概需要做什么了!提示信息如下:
To solve this problem, add the platform "darwin" to the "binaryTargets" attribute in the "generator" block in the "schema.prisma" file:
generator client {
provider = "prisma-client-js"
binaryTargets = ["native"]
}
Then run "prisma generate" for your changes to take effect.
Read more about deploying Prisma Client: https://pris.ly/d/client-generator
at DefaultLibraryLoader.getLibQueryEnginePath (/Users/lijing/coder/node/nestjs/scoring-system/dist/main.js:2:909505)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async DefaultLibraryLoader.loadLibrary (/Users/lijing/coder/node/nestjs/scoring-system/dist/main.js:2:906429)
at async LibraryEngine.loadEngine (/Users/lijing/coder/node/nestjs/scoring-system/dist/main.js:2:915306)
at async LibraryEngine.instantiateLibrary (/Users/lijing/coder/node/nestjs/scoring-system/dist/main.js:2:914257)
at async LibraryEngine.getConfig (/Users/lijing/coder/node/nestjs/scoring-system/dist/main.js:2:918644)
at async a._getActiveProvider (/Users/lijing/coder/node/nestjs/scoring-system/dist/main.js:2:1008726) {
clientVersion: '4.6.1',
errorCode: undefined
}
直接根据提示查阅官网www.prisma.io/docs/concep…
根据官网描述,如果只是在本地环境进行使用,可以不指定,或者简单指定binaryTargets为"native",很不幸,我经过测试指定为"native"还是不行,会出现一样的问题。官网还说,可以一次性指定多个,我把windows,mac,linux全部都涵盖进去了
generator client {
provider = "prisma-client-js"
binaryTargets = ["darwin","windows","linux-musl"]
}
这里需要注意,每次修改完prisma.schema文件后需要重新执行一次prisma generate命令来生成新的Client!这个client默认是放在node_modues/.prisma/client中,并在node_modules/@prisma/client进行了导出,所以实际上我们不用更改已经写好的代码(代码中我们都是从@prisma/client这里引入PrismaClient)。
我在想,这回总该没问题了吧!!然后显示再次打了我的脸!
提示信息说我已经加入了多个平台的生成器配置,但是还是出现了问题!!让我去官方github提issue!!!这把我搞懵逼了,我不信这个邪,我在想这么简单的打包操作怎么会有这么多坑啊!我决定再研究尝试一下!
问题还是出在prisma,我再次查看了generator相关的信息,我想试试自定义导出位置,会不会是因为node_modules打包后,没有将我们的client导出出来,我做了如下配置,主要是增加了output,自定义导出路径:
generator client {
provider = "prisma-client-js"
output = "../src/generated/client"
binaryTargets = ["darwin","windows","linux-musl"]
}
而后执行
➜ prisma generate
可以看出新生成的client已经在src目录下生成了
此时我们需要修改自己的代码,修改PrismaClient的引入位置
再次打包,并进入dist目录执行main.js!
➜ node main.js
我的个乖乖,终端又开始一顿乱码输出!
不过这次的问题不再是Prisma generator的问题了!这是环境变量的配置问题,我感觉到希望就在眼前了!我把url这不使用env,直接写死在里面
datasource db {
provider = "mysql"
url = "mysql://root:xxxxxx(这里是密码)@localhost:3306/nestjs"
}
最后真是让我喜极而泣啊!终于成功了!打包成功!
至此,打包完成!
二、总结
- nestjs默认的build打包不会打包依赖,只是简单将ts转为js放到dist目录,想要实现依赖打包需要配置webpack打包方式,并一定注意IgnorePlugin中的配置,当遇到Can't resolve ...的问题时,可以尝试添加到lazyImports中。
- PrismaClient客户端的生成依赖于schema.prisma文件中的generator配置,一定要记得要配置自定义导出及目标平台。
- schema.prisma中的数据库参数配置不要使用env的方式,直接配置实际值就行。
最后交付的文件如下,不到5M的大小,部署时直接运行node main.js即可
三、其它补充
打包的问题困扰了我好几天,因为一直无果,我考虑了其他方式,这里只做一个简单的介绍
1. docker容器部署
Dockerfile如下(文件就在项目根目录下):
FROM node:lts-alpine
WORKDIR /app
COPY package.json .
RUN npm config set registry https://registry.npm.taobao.org
RUN npm install
COPY . .
COPY dist ./dist
EXPOSE 7777
CMD ["node", "dist/main.js"]
可以看到生成镜像的体积在1.5个G左右,非常庞大!运行是没问题的,相当于把整个项目copy过去了,还得加上底层的linux核心!
2. pkg打包
需要先安装pkg
npm install pkg -g
可以看到,入口文件我使用的是build后的main.js,因为我如果使用源代码下的src/main.ts,打包会报错
估计是没法pkg没法直接打包,使用src/main.ts作为入口时会出现一下信息
使用dist/mian.js这种方式打包有exe产生,打包出来的文件大概200多M,但是放到windows下运行依然会出错。
报错如下:
这个问题在网上搜了一下,没有发现解决方案,希望有大佬可以解疑答惑。
至此,nestjs + prisma + mysql的打包部署已经讲完了,相比来说,我更喜欢使用webpack的方式进行打包,打包出来的文件只有5M不到,只要有node环境,直接使用node运行main.js文件,就完成了部署,非常简单方便!希望本篇文章能帮到有同样疑惑的你,如果有所帮助,记得给个关注和好评,谢谢!