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 端点(例如 /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=previewNEXT_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_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 端点代码中的示例:

// 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 已配置好 assetPrefixbasePath,使静态资源与路由在 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)。

评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION