1. 为什么为 ChatGPT App 选择 Vercel
在之前的讲座中,我们在本地运行了 GiftGenius,并通过 Dev Mode 和隧道把它接入 ChatGPT。现在是迈向“成熟”生产环境的又一步,把同样的代码迁移到 Vercel。
此时你已经有一个可工作的 GiftGenius(我们的教学示例 App)。在本地,它基于 Next.js 16,提供 MCP 端点(例如 /api/mcp),并使用官方的 ChatGPT Apps SDK Next.js Starter 搭建。
你也可以选择“我租一台 VPS,手动安装 Node、nginx 并自己配置”的道路,但对 Next.js 来说,这就像在 2025 年还用纯 document.write 写前端。能跑,但明显是在给自己找麻烦。
Vercel 对我们有多方面的好处。
首先,它对 Next.js 有原生支持:会自动配置构建、SSR、静态资源、edge 层和 serverless 函数。对 ChatGPT App 来说尤其方便,因为小部件和 MCP 端点可以一键一起发布,且运行在同一基础设施上。
其次,Vercel 开箱即用提供 CI/CD:你把 Git 仓库接上——每次 push 都会创建一个带唯一 URL 的不可变部署(immutable deployment)。来自 main 分支的部署被视为 production,其他分支为 preview。
第三,Vercel 在环境与机密管理方面体验很好。它明确区分 Development、Preview、Production 三种环境变量,采用加密存储,并可便捷注入到 Next.js 中。这正是 ChatGPT App 所需要的:根据环境切换密钥和 MCP 服务器的 URL。
第四,Vercel 提供便捷的回滚:如果新版本出现问题,可以快速将上一次成功的部署提升为生产,恢复系统正常。这降低了“部署恐惧”,鼓励小步快跑、频繁发布。
最后,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 的讲座中,你已经克隆了 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 不会进入仓库。Next.js Starter 的 .gitignore 通常已包含它们,但最好再确认一次:
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 的发行说明,最低版本是 18.18.0。通常在 package.json 中加上:
{
"engines": {
"node": ">=18.18.0"
}
}
Vercel 会选用与你应用兼容的 LTS 版 Node。
如果以上都就绪,推送最新代码到 Git,就可以前往 Vercel 了。
4. 第一次在 Vercel 导入项目
现在进入 Vercel 的 Web 界面。如果还没注册,现在就去注册。
登录 Vercel,点击“New Project”,从列表中选择你的 giftgenius 仓库。此时 Vercel 会在后台检测仓库内容,几乎总能自动识别为 Next.js 项目并套用相应预设。
在项目设置中,Vercel 会建议:
- Framework = Next.js;
- Build Command = npm run build(或 pnpm build/yarn build);
- Output Directory — 默认 .next(无需修改)。
首次部署可以暂时不配置环境变量(我们稍后单独添加)。点击“Deploy”——Vercel 会克隆仓库、安装依赖、执行 npm run build,如果一切顺利,就会创建首个部署,地址类似 https://giftgenius-xyz.vercel.app。
有个重要概念:每个部署都是不可变的(immutable)。之后你再 push 更改,Vercel 会创建一个新的部署并分配新 URL,旧的会保留在历史记录里。生产域名(例如 giftgenius.vercel.app 或你的自定义域名)指向某个具体部署,你可以切回去实现回滚。
示意如下:
flowchart LR
A[GitHub 仓库
giftgenius] -->|git push| B[Vercel build]
B --> C[预览部署 #1
唯一 URL]
B --> D[预览部署 #2
唯一 URL]
D --> E[生产别名
giftgenius.vercel.app]
main 分支通常被视为生产分支,其它分支为 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 | 所有非生产分支的部署 | staging / test |
| Production | 来自生产分支(通常为 main)的部署 | “生产” .env.prod |
与本地 .env.local 不同的是,Vercel 会加密保存这些值,并自动将它们作为 process.env.MY_VAR 注入到 Next.js 代码中。
一定要理解 NEXT_PUBLIC_ 前缀。凡是以 NEXT_PUBLIC_ 开头的变量都会出现在浏览器打包产物中,任何用户都能在 DevTools 中看到。它适用于公开配置(例如 NEXT_PUBLIC_ENV=preview、NEXT_PUBLIC_API_BASE_URL=https://giftgenius.vercel.app),但绝不应用于类似 OPENAI_API_KEY 这样的密钥。
机密请使用不带 NEXT_PUBLIC_ 的变量名,并且仅在服务端读取:例如在 route handlers、MCP 工具中等。
6. 为 GiftGenius 配置环境变量:示例
看看我们的教学示例 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 端点代码中的示例:
// 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 会为每次 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 配置中把端点从隧道 URL 临时改为 preview URL,观察已构建但尚未进入生产的 GiftGenius 行为。
常见做法:为某个功能创建分支 feature/smart-recommendations,push 更改——Vercel 就会给出 preview 链接。你去 Dev Mode,把 URL 改为该链接,用 GPT 验证流程(礼物推荐、卡片展示、MCP 工具调用)。只有当一切正常时,才合并进 main。Production 这边继续稳定运行。
流水线心智图:
flowchart TD
A[本地开发
localhost + 隧道] --> B[git push
feature/*]
B --> C[预览部署
preview URL]
C --> D[ChatGPT Dev Mode
App → preview URL]
C --> E[代码评审 / 测试]
E --> F[合并到 main]
F --> G[生产部署
prod-URL]
G --> H[ChatGPT Prod App
App → prod-URL]
8. 生产部署与回滚
当你将改动合并进 main(或你设定的生产分支),Vercel 会创建生产部署,并把生产别名(如 giftgenius.vercel.app 或你的自定义域名)指向它。
此时,ChatGPT 的生产 App(你稍后会创建)应配置为生产 URL。你可以继续在 Dev Mode 中用隧道或 preview URL 做实验;而 ChatGPT Store 中的普通用户将访问生产环境。
不可变部署的优势在于回滚非常简单。如果新版本不理想(比如 MCP 工具在真实数据上报错),无需在生产紧急修复。打开 Vercel 的部署列表,选择上一个成功版本,点击类似“Promote to Production”的按钮——K8s 和 Lambda 会在幕后切换,你的域名就会回到稳定版本。
在 CLI 中也可以通过 vercel rollback 来自动化。但在我们课程的层面,理解核心理念就够了:每个部署都是独立的制品,而生产别名可以指向其中任意一个。
9. Vercel 上的 Next.js 16 + MCP 特性
对 Vercel 而言,你在 Next.js 中的 MCP 端点就是一个 serverless 函数(或若配置为 edge,则为 edge 函数)。它的生命周期很短:请求到来时“唤醒”、处理完后就终止。除非使用外部数据库或存储,否则不能在调用之间保存状态。
这对 MCP 尤为关键:如果你把会话历史存到 route.ts 中的全局数组 let history = [],它会在每次冷启动时被清空。要保存状态,需要使用外部系统(KV、Postgres 等),这会在后续模块中讨论。
第二点是执行超时。Vercel 免费计划的 serverless 函数有时间限制(撰写材料时,Hobby 约 10 秒,Pro 更长)。对于 LLM 请求,尤其是串联多个 MCP 工具时,这个时间可能偏短。
在 Next.js 16 中,你可以为 route handlers 设置 maxDuration,在计划允许范围内请求更长的执行时间:
// 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 的嵌套 iframe 中能正常工作。由此所有请求都会发到你的域名,而不是 sandbox。部署到 Vercel 后,这个配置保持不变,你能“开箱即用”地获得正确的小部件行为。
10. 部署后的 ChatGPT 集成
虽然这更接近 Store 与生产运维的主题,但部署后的接入流程很简单,也很适合现在就讲。
先把 GiftGenius 部署到 Vercel,获得生产 URL。然后在 ChatGPT 的 Dev Mode 中单独创建一个应用(例如 GiftGenius Prod),在其设置中将端点指向该 URL(更精确地说,是 MCP 端点,如 https://giftgenius.vercel.app/api/mcp,依据 OpenAI Apps SDK Deploy 指南)。
开发阶段继续使用指向隧道或 preview URL 的 Dev App。为了测试日/周构建,你还可以创建 Staging App,把它绑定到一个稳定的 preview 别名。最终形成三层结构:
Dev App → 本地隧道或 dev-URL(不稳定)
Staging App → 稳定的 preview/staging URL(Vercel)
Prod App → 生产环境 URL(Vercel)
作为参考,我们汇总成表:
| 对象 | URL / 在 Vercel 上的部署 | Vercel Scope | 访问者 |
|---|---|---|---|
| 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”,应用也构建成功,但生产环境的 MCP 工具返回 500,内容是“Missing OPENAI_API_KEY”。原因很简单:Vercel 不知道你本地的 .env.local。需要把相同变量单独配置到 Vercel 项目的设置里(并分配到正确的 scope:Preview、Production)。
错误 №2:把敏感数据放到 NEXT_PUBLIC_。
有时为了“先能跑”,开发者会写 NEXT_PUBLIC_OPENAI_API_KEY,这样就能在客户端访问密钥。结果密钥被打进 JS 包,任何人都能拿到。这不仅是不佳实践,更是直通泄露与封禁的道路。所有机密——一律不用该前缀,且只在服务端使用。
错误 №3:本地与 Vercel 环境不一致。
本地可能用一个产品服务 URL(http://localhost:4000),Vercel 上用另一个(https://api.gifts-staging.com),生产又是第三个。如果不仔细维护环境变量清单,不检查 Preview/Production 的配置是否正确,就很容易出现生产小部件访问了 staging 后端、staging 小部件访问了生产后端的情况。解决办法是简单的纪律:记录所有需要的变量,并在每个环境上逐一核对。
错误 №4:忽略 MCP 端点的执行时间限制。
在本地你可能会等某个慢速外部系统 30 秒而不觉得有问题。在 Vercel 上,同一个函数可能在 10–15 秒后就被超时终止,ChatGPT 会看到错误。如果你没有设置 maxDuration,也没有关注 MCP 工具的执行时长,在生产中就会变成偶发性故障。
错误 №5:尝试把 MCP 的状态存到 serverless 函数内存里。
有时很想把对话历史或推荐缓存放到 route handler 文件中的全局变量 let cache = {}。在本地 dev 服务器长时间运行的情况下,这看起来“能用”。但在 Vercel 上,每个 serverless 函数生命周期很短且经常重建。结果有的请求“看到”旧缓存,有的看到新缓存,还有的为空。这会产生难以复现的诡异 bug。状态需要外部数据库或 KV 存储;在本讲的层面,最好把 MCP 端点视为无状态(stateless)。
GO TO FULL VERSION