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=preview、NEXT_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_URL 或 PRODUCTS_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 已設定好 assetPrefix 與 basePath,讓靜態資源與路由能在 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)。
GO TO FULL VERSION