將 Mororepo 中某個專案部署到 Vercel
前置作業及說明
因為我的 api server 是放在使用 turbo + pnpm 的 monorepo 中, 然後我想要只 deploy api server 到 vercel, 所以紀錄一下如何設定
其中 api server 使用的是 NestJs (相關文章可以參考 Docs 中的 NestJs 部分, 這邊可以直接在 apps/api 中直接使用 @nestjs/cli
new 一個基本的 nestjs project, 然後在 app.controller.ts
中寫一個簡單的 /
api 來當範例
// ...
@Controller()
export class AppController {
@Get('/')
getRoot(@Response({ passthrough: true }) res: ExpressResponse) {
return res.end('Hello World!');
}
}
build 的部分有用到 shared package 中的其他 project, 所以使用 turbo 來 build
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["**/.env", "**/.env.*local", "**/.env.*staging", "**/.env.*production", ".env", ".env.staging", ".env.production"],
"tasks": {
"build:production": {
"cache": false,
"dependsOn": ["^build:production"],
"outputs": ["dist/**"],
"inputs": ["$TURBO_DEFAULT$", ".env", ".env.production"]
}
}
}
root package.json 中設定 turbo build 的指令
{
"name": "monorepo",
"version": "0.1.0",
"author": "JimCat",
"license": "MIT",
"private": true,
"scripts": {
"build:production": "turbo build:production"
},
"packageManager": "pnpm@9.12.1"
}
設定對應的 packages, apps 中 project 的 package.json
{
"name": "api",
"version": "0.0.1",
"description": "API server using NestJS",
"private": true,
"scripts": {
"build:production": "dotenv -e .env -- nest build"
}
}
{
"name": "@repo/shared-package",
"main": "dist/index.js",
"scripts": {
"build:production": "tsc"
}
}
Vercel settings
在根目錄新增 vercel.json 以及 api folder, 裡面新增一個 entry point main.js
{
"buildCommand": "pnpm run api:build",
"installCommand": "pnpm install",
"outputDirectory": "apps/api",
"functions": {
"api/main.js": {
"includeFiles": "apps/api/dist/**/*"
}
},
"rewrites": [{ "source": "/(.*)", "destination": "/api/main.js" }],
"github": {
"enabled": false
}
}
module.exports = require('../apps/api/dist/src/main.js');
指令設定可以參考 Vercel JSON Configuration, github 的 enabled 設為 false
是因為不想每個更動都去 trigger vercel 的 deploy, 而是透過各自 branch/project 建立的 github actions 來 deploy
舊的 vercel.json 寫法:
{
"version": 2,
"buildCommand": "pnpm run api:build",
"installCommand": "pnpm install",
"outputDirectory": "apps/api",
"builds": [
{
"src": "apps/api/dist/src/main.js",
"use": "@vercel/node"
}
],
"routes": [
{
"src": "/(.*)",
"dest": "apps/api/dist/src/main.js",
"methods": ["GET", "POST", "PUT", "DELETE"]
}
],
"github": {
"enabled": false
}
}
因為 builds 以及 routes 已經被棄用, 這邊只是做個對照
而 api/main.js
是對應到 serverless 原本設定 builds 改為 functions 的部分
For all officially supported runtimes, the only requirement is to create an api directory at the root of your project directory, placing your Serverless Functions inside.
詳細可參考 functions description
新增 .vercelignore
/node_modules
!/dist
Install Vercel CLI
npm i -g vercel
Deploy to Vercel
在開始前先 build 相關 project 得到 build file, 這裡直接使用 turbo 來 build, 可以同時將 api 有使用到其他 packages 的部分一起 build
turbo build:production --filter=api
接著 deploy, 可以選擇要不要直接 sync github project, 或者之後再設定 (反正第一次會失敗(如果 server 需要 env 的話))
import env
到 vercel 選取剛剛 deploy 的那個專案, 至 Settings -> Environment Variables, 直接 import 整個 .env file 上去
如果是一開始就手動在 vercel 操作建立 project, 在 import env 那邊目前沒看到可以直接先上傳整個 env file (也可能我眼殘但我沒找到), 反而要一個一個 key, 太懶了所以我選擇先用 cmd 跑一次再直接去 dashboard 一次上傳
然後再重新 deploy 一次, 成功畫面如下
source 的結構會類似這樣, 會看到最終用來 deploy 的資料在 /api
中
Use GitHub Actions to deploy
因為我們只需要 deploy api, 不需要去影響其他 project, 所以另外開一個 api branch
, 建立 .github/workflows/deploy-vercel.yml
撰寫 actions, 並只針對 api
branch 執行
在 vercel project settings 中可以連接對應的 GitHub repository
建立 GitHub Actions
name: Deploy API to Vercel
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_API_PROJECT_ID }}
on:
push:
branches:
- api
jobs:
deploy:
if: github.ref == 'refs/heads/api'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: 9.12.1
run_install: false
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: Install dependencies & Build
run: |
pnpm install turbo --global
pnpm install
turbo build:production --filter=api
- name: Install Vercel CLI
run: |
pnpm install -g vercel
- name: Deploy to Vercel
env:
VERCEL_TOKEN: ${{ secrets.TURBO_TOKEN_FOR_GITHUB_ACTION }}
run: |
vercel pull --yes --environment=production --token=$VERCEL_TOKEN
vercel --prod --token=$VERCEL_TOKEN
在 GitHub repository 的 Settings → Secrets 中新增 VERCEL_ORG_ID
, VERCEL_API_PROJECT_ID
, TURBO_TOKEN_FOR_GITHUB_ACTION
其中為了讓 action 部屬到對應的 vercel project 上, 需要設定 VERCEL_ORG_ID
, VERCEL_API_PROJECT_ID
(可以參考這裡), 以及透過 vercel token 在 vercel build 的時候可以取得已經 import 的 env 資料
ORG_ID
的位置在 vercel.com/account > settings > general > Vercel ID
PROJECT_ID
的位置在 vercel.com/dashboard > project > settings > general > Project ID
這樣當 api
branch 更新時, 就會自動部屬到 vercel 上了
refs: