Skip to main content

將 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 來當範例

apps/api/src/app.controller.ts
// ...
@Controller()
export class AppController {
@Get('/')
getRoot(@Response({ passthrough: true }) res: ExpressResponse) {
return res.end('Hello World!');
}
}

build 的部分有用到 shared package 中的其他 project, 所以使用 turbo 來 build

turbo.json
{
"$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 的指令

package.json
{
"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

apps/api/package.json
{
"name": "api",
"version": "0.0.1",
"description": "API server using NestJS",
"private": true,
"scripts": {
"build:production": "dotenv -e .env -- nest build"
}
}
packages/shared-package/package.json
{
"name": "@repo/shared-package",
"main": "dist/index.js",
"scripts": {
"build:production": "tsc"
}
}

Vercel settings

在根目錄新增 vercel.json 以及 api folder, 裡面新增一個 entry point main.js

vercel.json
{
"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
}
}
main.js
module.exports = require('../apps/api/dist/src/main.js');

指令設定可以參考 Vercel JSON Configuration, github 的 enabled 設為 false 是因為不想每個更動都去 trigger vercel 的 deploy, 而是透過各自 branch/project 建立的 github actions 來 deploy

note

舊的 vercel.json 寫法:

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 的話))

1.png

import env

到 vercel 選取剛剛 deploy 的那個專案, 至 Settings -> Environment Variables, 直接 import 整個 .env file 上去

tip

如果是一開始就手動在 vercel 操作建立 project, 在 import env 那邊目前沒看到可以直接先上傳整個 env file (也可能我眼殘但我沒找到), 反而要一個一個 key, 太懶了所以我選擇先用 cmd 跑一次再直接去 dashboard 一次上傳

然後再重新 deploy 一次, 成功畫面如下

2.png

source 的結構會類似這樣, 會看到最終用來 deploy 的資料在 /api

3.png

Use GitHub Actions to deploy

因為我們只需要 deploy api, 不需要去影響其他 project, 所以另外開一個 api branch, 建立 .github/workflows/deploy-vercel.yml 撰寫 actions, 並只針對 api branch 執行

在 vercel project settings 中可以連接對應的 GitHub repository

4.png

建立 GitHub Actions

.github/workflows/deploy-vercel.yml
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

5.png

其中為了讓 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: