CodeGym /課程 /ChatGPT Apps /在 Vercel 上部署:儲存庫、環境變數、preview → production

在 Vercel 上部署:儲存庫、環境變數、preview → production

ChatGPT Apps
等級 7 , 課堂 3
開放

1. 為什麼 ChatGPT App 要用 Vercel

在前幾課我們已經把 GiftGenius 在本機啟動,並透過 Dev Mode 與隧道連到 ChatGPT。現在該再往「成熟」的生產環境邁進一步,把同一份程式碼搬到 Vercel。

此時你應該已有可運作的 GiftGenius(我們的教學 App)。在本機它跑在 Next.js 16,有 MCP endpoint(例如 /api/mcp),並以官方的 ChatGPT Apps SDK Next.js Starter 為基礎。

當然你也可以走「我租 VPS、手動安裝 Node、nginx,自己全部設定」這條路,但對 Next.js 而言,這大概像是 2025 年還在用純 document.write 寫前端。能動,但你顯然把自己的人生弄得更複雜。

Vercel 對我們有幾個好處。

首先,它原生理解 Next.js:自動設定建置、SSR、靜態資源、edge 層與無伺服器函式。對 ChatGPT App 特別方便,因為小工具與 MCP endpoint 可以一鍵部署,並共用同一套基礎設施。

其次,Vercel 內建 CI/CD:你連上 Git 儲存庫——每次 push 都會建立新的不可變部署(immutable deployment),附上唯一的 URL。來自 main 的視為 production,其它分支則是 preview。

第三,Vercel 在環境與密鑰管理上很好用。它明確將環境變數分為 Development、Preview 與 Production,加密保存,並能方便地注入到 Next.js。這正是 ChatGPT App 所需,因為金鑰與 MCP 伺服器的 URL 需隨環境而變。

第四,Vercel 有好用的回滾:若新版本不理想,你能迅速把先前成功的部署升到 production,讓系統恢復運作。這降低了「部署恐懼」,也鼓勵更小、更頻繁的發布。

最後,Vercel 是 Next.js 的創建公司。他們讓 Next.js 與自家伺服器彼此契合。使用 Vercel 你會一次又一次感受到一切順滑、幾次點擊就搞定。保證你會喜歡。

2. 起點:GiftGenius 專案結構

依照課程計畫,我們的 GiftGenius 放在同一個儲存庫中。組織方式有兩種,兩者在 Vercel 都可行:

1) 多應用的 Monorepo——例如:

giftgenius/
  apps/
    web/   # Next.js(小工具 + MCP)
    mcp/   # 獨立的 MCP 伺服器(如果你把它拆出來)

2) 單一 Next.js 專案,小工具與 MCP 都放在一起(入門更簡單,官方 starter 也是這樣):

giftgenius/
  app/
    page.tsx         # 小工具
    api/
      mcp/route.ts   # MCP 端點
  next.config.mjs
  package.json
  ...

在模組 2 的課程裡,你已經 clone 了 Apps SDK Starter、安裝相依套件並執行 npm run dev。現在我們假設:

  • 專案已在 Git(GitHub / GitLab / Bitbucket);
  • 你在本機使用 .env.local 儲存金鑰(如 OPENAI_API_KEY 等);
  • ChatGPT Dev Mode 已連到你的隧道。

我們的目標——讓相同的程式碼能在 Vercel 建置並運作,且讓 ChatGPT 不是連隧道,而是連固定的 HTTPS 網域,例如 https://giftgenius.vercel.app

3. 部署前準備儲存庫

在 Vercel 按下「New Project」之前,先把儲存庫稍微整理一下。這些簡單步驟能在之後省下大量時間。

首先,確認 .env.local.vercel 不會被提交到儲存庫。在 .gitignore(Next.js Starter 通常已包含),但最好再檢查一次:

node_modules
.next
.env.local
.vercel

.env.local 是你的本機設定與機密。它永遠不該進 Git,尤其當裡面有 OPENAI_API_KEY 或資料庫金鑰時。到了 Vercel,我們會在 UI 中分開儲存這些機密。

其次,檢查 package.json。對 Vercel 而言,scripts 需正確:

{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start"
  }
}

Vercel 預設會呼叫 npm run build(若你用 pnpm,則是 pnpm build)。它必須能無錯建置專案。

再者,確保 Node 版本有指定,且符合 Next.js 16。根據 Next.js 16 的 release notes,最低版本是 18.18.0。通常在 package.json 加上:

{
  "engines": {
    "node": ">=18.18.0"
  }
}

Vercel 會選用與你的應用相容的 LTS 版本。

若以上都完成,就把最新程式碼 push 到 Git,接著前往 Vercel。

4. 將專案首次匯入 Vercel

現在打開 Vercel 的網頁介面。若你尚未註冊,現在正是時候。

登入 Vercel,點「New Project」,在列表中選擇你的儲存庫 giftgenius。此時 Vercel 會在背後檢查儲存庫內容,幾乎總能自動判定這是 Next.js 專案,並套用對應的 preset。

Vercel 的專案設定會建議:

  • Framework = Next.js;
  • Build Command = npm run build(或 pnpm build/yarn build);
  • Output Directory = 標準的 .next(不需更改)。

第一次部署可以先不填環境變數(我們稍後補)。按下「Deploy」——Vercel 會 clone 儲存庫、安裝相依、執行 npm run build,若成功,就會建立第一個部署,網址類似 https://giftgenius-xyz.vercel.app

有個重點要先理解:每個部署都是不可變的(immutable)。之後你再 push 更動,會建立新的部署與新的 URL,舊的則留在歷史中。Production 網域(例如 giftgenius.vercel.app 或你的自訂網域)指向某個特定部署,你可以切回舊版來回滾。

示意如下:

flowchart LR
    A[GitHub 儲存庫
giftgenius] -->|git push| B[Vercel build] B --> C[Preview 部署 #1
唯一的 URL] B --> D[Preview 部署 #2
唯一的 URL] D --> E[Production 別名
giftgenius.vercel.app]

Git 分支 main 通常視為 production 分支,其餘是 preview。不過你可以調整設定。

5. Vercel 上的環境變數

現在你的第一個部署多半還不太能用:缺少 OPENAI_API_KEY、MCP 伺服器無法呼叫外部 API 等。該來處理環境變數了。

在 Vercel,環境變數位於 Settings → Environment Variables。這裡同時能看到三個 scope:Development、Preview 與 Production。

給你一張心智模型的小表:

Scope 用在何處 本機對應
Development vercel dev 與透過 Vercel CLI 的本機開發 .env.local
Preview 所有非 production 分支的部署 staging / 測試
Production 來自 production 分支(通常 main)的部署 正式的 .env.prod

與本機的 .env.local 不同,Vercel 會把值加密保存,並自動在 Next.js 中以 process.env.MY_VAR 注入。

務必理解 NEXT_PUBLIC_ 前綴。所有以 NEXT_PUBLIC_ 開頭的變數都會出現在瀏覽器 bundle 中,任何使用者都能透過 DevTools 查看。這對公開設定很有用(例如 NEXT_PUBLIC_ENV=previewNEXT_PUBLIC_API_BASE_URL=https://giftgenius.vercel.app),但對於像 OPENAI_API_KEY 這種金鑰則是完全不可取。

機密請使用不含 NEXT_PUBLIC_ 的名稱,且只在伺服器端讀取:例如在 route handlers、MCP 工具等。

6. GiftGenius 的 env 設定:範例

來看看我們的教學 GiftGenius 需要哪些環境變數。

最小集合可能如下:

  • OPENAI_API_KEY —— 呼叫模型/MCP 用戶端的金鑰;
  • APP_BASE_URL —— 應用的基底 URL(https://giftgenius.vercel.app 或 preview URL);
  • 可能還有 GIFTDATA_API_URLPRODUCTS_API_URL,若你有外部目錄的話。

在本地開發中,這些放在 .env.local

OPENAI_API_KEY=sk-local-...
APP_BASE_URL=http://localhost:3000
PRODUCTS_API_URL=https://dev-api.gifts.example.com

在 Vercel,前往 Settings → Environment Variables,將同樣的鍵與值新增到對應的 scope。

MCP endpoint 程式碼中的使用範例:

// app/api/mcp/route.ts
import { NextRequest } from 'next/server';

const apiKey = process.env.OPENAI_API_KEY!; // 實際程式碼請不要這樣做,至少要檢查 :)

export async function POST(req: NextRequest) {
  if (!apiKey) {
    return new Response('Missing OPENAI_API_KEY', { status: 500 });
  }
  // 使用 apiKey 呼叫 OpenAI 或其他服務...
}

小工具可以在伺服器端利用 APP_BASE_URL 來產生絕對連結,考慮 ChatGPT 的 iframe 與 starter 模板中 assetPrefix/basePath 的設定。

若它需要公開的 API URL(例如在客戶端以 window.fetch 呼叫你的後端),可以建立 NEXT_PUBLIC_API_BASE_URL。但絕對不要使用 NEXT_PUBLIC_OPENAI_API_KEY

7. Preview 部署:staging 加強版

接著談最令人愉悅的部份:Preview 部署。當你連上 Git 儲存庫後,Vercel 會自動為每個非 production 分支的 push 或每個 Pull Request 建立 Preview 部署。每個部署都有唯一的 URL,例如:

https://giftgenius-git-feature-new-layout-username.vercel.app

這些部署使用 Preview scope 的環境變數,因此你可以設定像這樣:

# Vercel 的 Preview 環境
APP_BASE_URL=https://giftgenius-staging.vercel.app
PRODUCTS_API_URL=https://staging-api.gifts.example.com

而不會和 production 混淆。

從 ChatGPT Dev Mode 的觀點,preview URL 是 staging 的完美候選。在你的 Dev App 設定中,你可以暫時把 endpoint 從隧道 URL 改成 preview URL,看看已建置版本的 GiftGenius 表現如何,但它仍不是 production 部署。

常見做法:為某個功能建立分支 feature/smart-recommendations,push 更動——Vercel 就會給你一個 preview 連結。你到 Dev Mode,把 URL 換成這個連結,測試 GPT 的流程(挑選禮物、卡片呈現、MCP 工具呼叫)。一切都 OK 後,再合併到 main。Production 此時仍安穩運作。

管線的心智圖:

flowchart TD
    A[本機開發
localhost + 隧道] --> B[git push
feature/*] B --> C[Preview 部署
preview-URL] C --> D[ChatGPT Dev Mode
App → preview-URL] C --> E[Code review / 測試] E --> F[合併到 main] F --> G[Production 部署
prod-URL] G --> H[ChatGPT Prod App
App → prod-URL]

8. Production 部署與回滾

當你把變更合併到 main(或你指定的 production 分支)時,Vercel 會建立 Production 部署,並把 production alias 掛到該部署上:giftgenius.vercel.app 或你的自訂網域。

此時你稍後會建立的 ChatGPT Prod App,應該設定為指向 production URL。而 Dev Mode 仍持續使用隧道或 preview URL;一般使用者在 ChatGPT Store 看到的是 production。

不可變部署的優勢是回滾非常簡單。若新版本失敗(例如 MCP 工具在真實資料上崩潰),你不必在 production 緊急修復。打開 Vercel 的部署列表,選擇先前成功的版本,按下類似「Promote to Production」的按鈕——遙遠的 K8s 與 Lambda 會被切換,而你的網域重新指向穩定版本。

透過 CLI 也能自動化,例如使用 vercel rollback。在本課程層級,理解概念即可:每個部署都是獨立產物,production alias 可以指向其中任一個。

9. Next.js 16 + MCP 在 Vercel 上的特性

在 Vercel 看來,你的 Next.js 中的 MCP endpoint 是一個無伺服器函式(或在你設定為 edge 時是 edge 函式)。它生命短暫:接到請求才啟動、處理完就結束。除非使用外部資料庫或儲存體,否則無法在呼叫之間保存狀態。

這對 MCP 至關重要:如果你把對話歷史存進 let history = [] 這種全域陣列(位於 route.ts)中,每次冷啟動都會被清空。要保存狀態請用外部系統(KV、Postgres 等),但那是之後的課程內容。

第二個面向——執行逾時。在免費方案中,Vercel 的無伺服器函式有時間限制(撰稿時約為 Hobby 方案 10 秒,Pro 更長)。對 LLM 請求,尤其是串接多個 MCP 工具時,可能不太夠。

Next.js 16,你可以為 route handlers 指定 maxDuration,明確向 Vercel 要更長的時間(在方案允許的範圍內):

// app/api/mcp/route.ts
export const maxDuration = 60; // 秒;在 Pro 上可到 300

export async function POST(req: Request) {
  // 長時間操作:呼叫 OpenAI、外部資料庫等
}

這不是「想跑多久就跑多久」的魔法按鈕,而是告訴 Vercel:「這支函式可能會跑久一點,請不要太早把它殺掉」的正確方式。

最後,別忘了 ChatGPT iframe 的特性。Apps SDK Starter 已設定好 assetPrefixbasePath,讓靜態資源與路由能在 web-sandbox.oaiusercontent.com 的 nested iframe 中正確運作。如此一來,所有請求都會打到你的網域,而不是 sandbox。部署到 Vercel 時,這份設定會保留,你就能開箱即用地獲得正確的小工具行為。

10. 部署後與 ChatGPT 的整合

雖然嚴格來說這更接近 Store 與生產環境的模組內容,但部署後與 ChatGPT 的整合其實很直接,也很適合現在就了解。

先把 GiftGenius 部署到 Vercel,取得 production URL。接著在 ChatGPT 的 Dev Mode 中建立一個獨立的 App,例如 GiftGenius Prod,並在其設定中把 endpoint 指向該 URL(更精確地說,是 MCP endpoint,如 https://giftgenius.vercel.app/api/mcp,依照 OpenAI Apps SDK Deploy 指南)。

開發階段你繼續使用看向隧道或 preview URL 的 Dev App。若要測試每日/每週的 build,可以再建立一個 Staging App,把它綁到固定的 preview alias。於是會得到三段式的配置:

Dev App     → 本機隧道或 dev-URL(不穩定)
Staging App → Vercel 上穩定的 preview/staging URL
Prod App    → Vercel 上的 production URL

作為參考,彙整成一張表:

項目 URL / Vercel 上的部署 Vercel 的範圍 誰會使用
Dev App 本機隧道 / vercel dev Development 你 / 團隊
Staging App 穩定的 preview 別名 Preview 團隊 / QA
Prod App giftgenius.vercel.app / 自訂網域 Production 使用者

這就是我們在模組開頭提到的 local / staging / prod 模型,只是現在綁定到 Vercel 與 ChatGPT Apps。這已經是成熟專案的架構,而不再是永遠的 localhost。

11. 在 Vercel 部署的常見錯誤

錯誤 №1:機密只放在 .env.local,沒放到 Vercel。
很常見的情況:本機一切正常,你自信地按下「Deploy」,應用也建置成功,但在 production 的 MCP 工具回傳 500,內容是「Missing OPENAI_API_KEY」。原因很簡單:Vercel 不知道你的本機 .env.local。務必把相同的變數加進 Vercel 專案設定(還要放進正確的 scope:Preview、Production)。

錯誤 №2:用 NEXT_PUBLIC_ 放敏感資料。
有時為了「先能動」會寫 NEXT_PUBLIC_OPENAI_API_KEY,好讓客戶端能讀到金鑰。結果金鑰被放進 JS bundle,任何使用者都看得到。這不只是壞習慣,而是直通外洩與金鑰被封鎖的捷徑。所有機密——一律不用該前綴,且只在伺服器端讀取。

錯誤 №3:本機與 Vercel 的環境設定不一致。
本機可能用一個產品 API URL(http://localhost:4000),Vercel 用另一個(https://api.gifts-staging.com),production 又是第三個。若不謹慎管理環境變數、未確認 Preview/Production 是否正確填入,很容易發生 production 小工具打到 staging 後端、staging 小工具打到 production 的情況。解法是簡單的紀律:列出所有需要的變數,並在每個環境核對。

錯誤 №4:忽略 MCP 端點的執行時間限制。
在本機你可能等待某個外部系統回應 30 秒而不覺得有問題。在 Vercel,這支函式可能在 10–15 秒就逾時,ChatGPT 會看到錯誤。若你沒有設定 maxDuration、也不監控 MCP 工具的執行時間,在 production 可能造成間歇性失敗。

錯誤 №5:嘗試把 MCP 狀態放在無伺服器函式的記憶體中。
有時很想把對話歷史或建議快取放進 route handler 檔案裡的全域變數 let cache = {}。在本機,dev server 長時間運行時看起來「似乎可行」。但在 Vercel,每個無伺服器函式壽命短、經常被重建。結果有的請求「看到」舊快取,有的看到新快取,還有的則是空的。這會產生難以重現的奇怪 bug。狀態請用外部資料庫或 KV;在本課程範圍,最好把 MCP endpoint 視為無狀態(stateless)。

1
任務
ChatGPT Apps, 等級 7, 課堂 3
上鎖
Health 端點 + 環境徽章 (local / preview / production)
Health 端點 + 環境徽章 (local / preview / production)
1
任務
ChatGPT Apps, 等級 7, 課堂 3
上鎖
型別安全的 env 設定(Zod),並區分 public/private
型別安全的 env 設定(Zod),並區分 public/private
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION