Migrating to Nx & Deploy on Vercel

AlfxjxAlfxjx
2022-05-09

最近发现 mongodb 有一个白嫖的云数据库可以用,准备开发一个统一的后台,用来承接一下个人的项目,并且给这个后台配置一个单独的管理后台。技术选型的时候,首先选择了之前比较熟悉的、使用了类似 Spring 的 Nest.js 来开发。也因此前端的选型选择了 Angular ,然后再部署到 vercel 上面,为了能够方便的部署和开发管理,选择了 GitHub Actions 和 Nx. 因此,本文的目标:将手头的一个 nest.js 项目添加到一个基于 Nx 的 monorepo , 添加一个 angular 的前端加入到 repo 中,使用 GitHub Actions 和 Vercel 进行自动化的部署。

  1. 项目地址: Alfxjx/home-admin
  2. 在线演示: home-admin

简介

首先是基于原本的脚手架,搭起了一个 nest.js 的项目,参考这个文章进行了部署。再进一步开发的时候发现了一些问题。因为选择了 mono-repo 的形式来管理前后端,需要对目前的项目进行一个迁移,并且迁移之后还是需要保持原本的 GitHub Actions。

Migration 迁移

Create an Nx repo

第一步是新建一个 Nx 的项目。可以参考文档,这里我们就直接新建一个空白的项目。

# create an empty workspace set up for building applications
npx create-nx-workspace --preset=apps

可以得到一个基本的模版,如下所示:

myorg/
├── apps/ # 存放项目的应用
├── libs/ # 存放自定义的库文件
├── tools/ # 自定义的一些脚本,类似数据库脚本。
├── workspace.json
├── nx.json
├── package.json
└── tsconfig.base.json

使用 VScode 开发,推荐安装 Nx 的官方扩展可以很方便的对项目进行配置。

nrwl.angular-console 看来原本是为了 ng 设计的

使用这个扩展可以唤起一个配置表单用来配置 serve / build 的配置参数,当然自己想在 CLI 里面输入也是可以的。

npx nx build <app-name>

Migrate the nest.js

框架搭好,之后需要做的就是把已经开发的项目迁移过来,参考文档,需要按照 Nx 的要求将之前的项目的文件迁移过来。有几个需要注意的:

  1. 首先把原本单体项目中的代码复制到新的项目的 /app/ 下
  2. 删掉之前的 package.json,但是把依赖复制到根目录的 package.json 下, 处理一下依赖的冲突,还可以顺便升级一下 deprecated 的依赖。
  3. 对于 nest.js 的项目需要一个 src/assets 目录,否则启动或者打包的时候会报错。
  4. 之前的 import 如果有使用绝对路径的需要进行修改。
  5. 配置文件,由于现在是一个子 app ,需要对 tsconfig.json, eslintrc.json 等文件进行修改,因为目前的 mono-repo 中, 主 repo 的根目录下有总的 config 文件,sub-app 需要在它的基础上进行修改:
  6. 需要新增一个 nx 的配置文件 project.json,添加一些 nx 的配置,例如开发模式,文件替换,输出位置,使用什么配置文件等。

迁移的过程中,按照功能点进行迁移验证,发现 Nx 对依赖的版本要求比较严格,下面的几个版本是绑定的,如果遇到一些问题的话,可以去看一下对应版本的 eslint-plugin-nx 的 peerDependency。除此之外,迁移的问题不是很大。

{
	"@nrwl/eslint-plugin-nx": "14.0.5",
	"@typescript-eslint/eslint-plugin": "~5.18.0",
	"@typescript-eslint/parser": "~5.18.0"
}

在 /app 里面的迁移做完之后,需要在 monorepo 中 注册一下,因为我们的项目是手动导入的,所以需要改一下 根目录下的 workspace.json,添加一个项目名称和路径的对应关系。

之前的 project.json 的配置也可以一股脑的放在最外层的 workspace.json 里面

迁移完成之后,运行一下来看看项目还能否正常使用:

# 除了指定端口之外,需要设置 inspect=false 这样就是原本的 http://localhost:3000 了
# inspect 参考 https://nodejs.org/en/docs/guides/debugging-getting-started/
npx nx serve <your-app-name> --port=3001 --inspect=false

Add new ng repo

添加一个新的项目就比较简单了,以添加一个 ng 项目为例

  1. 首先安装依赖 npm install -D @nrwl/angular
  2. 类似于 ng generate , nx g @nrwl/angular:app <appName>
  3. 事实上也没事别的需要做的,框架完成了接下来的任务。

Add utils / libs

使用 mono repo 的模式,一个很大的优点就是可以把通用的库文件拆分出来进行管理,并且又不会出现 multi repo 模式管理时,繁琐的发包任务。这里我们以一个通用的组件库和一个工具方法集合为例。

lib 组件库模式

创建一个 ng-lib : nx g @nrwl/angular:lib components,新增一个名为 components 的组件库,然后在 src 下可以添加组件,记得在 src/index.ts 进行导出:

export { Button } from "./libs/Button/index";
export type { ButtonProps } from "./libs/Button/index";

在 workspace.json 中,发现新增对应的配置。

"components": {
  "root": "libs/components",
  "sourceRoot": "libs/components/src",
  "projectType": "library",
  "tags": [],
  "targets": {
    "lint": {
      "executor": "@nrwl/linter:eslint",
      "outputs": ["{options.outputFile}"],
      "options": {
        "lintFilePatterns": ["libs/components/**/*.{ts,tsx,js,jsx}"]
      }
    },
    "test": {
      "executor": "@nrwl/jest:jest",
      "outputs": ["coverage/libs/components"],
      "options": {
        "jestConfig": "libs/components/jest.config.ts",
        "passWithNoTests": true
      }
    }
  }
},

在 tscofig.json 里面则是加入了对 path 的别名:

    "paths": {
      "@home-mono/utils": ["./libs/utils/src/index.ts"],
      "@home-mono/components": ["./libs/components/src/index.ts"]
    }

这样在 app/home-admin-web 的项目中引用的时候就可以:

import { Button } from "@home-mono/components";

utils 工具函数库

和上面的组件类似,不过是一个纯 ts lib :

npx create-nx-workspace <utils> --preset=ts

其他的工作和 上面的 components lib 类似。

Deploy 部署

至此,我们已经完成迁移的工作,可以本地尝试一下 build, 应该是可以的。后面就是完成部署的工作。

GitHub Actions

原本的 nest.js 的项目是通过 GitHub actions 部署到 vercel 。这里需要对 GitHub actions 的配置进行一点小修改即可。注意这里 nest.js 是需要配置 vercel.json, 指明文件的目录的。和参考的文章 1 中配置一致。

- run: npm ci
- run: npx nx build home-admin-server --if-present
- name: Deploy to Vercel
  run: npx vercel --token ${VERCEL_TOKEN} --prod
  env:
    VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
    VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
    VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}

Vercel git web hooks

前端的项目部署起来就比较简单,这里以 angular 的为例,参考一下 Nx 官网 的 next.js 的例子,发现需要在推送分支的时候,覆盖一下 vercel 的默认配置。

# BUILD COMMAND
nx run home-admin-web:build:production
# OUTPUT DIRECTORY
dist/apps/home-admin-web

看起来很简单,但是真的 push 才发现,由于我们的 mono repo 是一个项目,前端的项目会由于后端项目的 vercel.json ,使得刚才的配置不生效,为了解决这个问题,新建了一个 release-ng 的分支,删除了 master 上的 vercel.json。这样就可以正常部署了。记得修改一下 vercel 上的 production branch

另外,可以使用 GitHub actions 来自动对 release-ng 进行 rebase, 减少发布的手动流程。


More 其他

Route rewrites

一个和本文关系不是很大的技巧,vercel 上面绑定自己的域名时,把一个二级域名用于多个项目是通过 cname 子域名来做的,如果想使用二级域名,可以在 mono repo 里面新建一个 next.js 的 Gateway 项目,利用 path rewrite 进行转发实现。

参考了 https://notes.ljl.li/portfolio-redesign/

module.exports = {
	async rewrites() {
		return [
			{
				source: "/blog/:slug",
				destination: "https://example.com/blog/:slug",
			},
		];
	},
};

结论

总之就可以将原本的多个项目整合到一个 mono repo 里面来管理与开发,对于 MEAN/MERN 架构的项目来说还是很方便的。 希望你喜欢。

References

  1. 基于 Vercel+Github Action 部署 Nest.js 项目
  2. https://nx.dev/
  3. 如何使用 Nx、Next.js 和 TypeScript 构建 Monorepo
  4. Deploying Next.js applications to Vercel
  5. Customizing the Production Branch
  6. Rewrites - Next.js
  7. https://notes.ljl.li/portfolio-redesign/