JavaRush /Курсы /ChatGPT Apps /Deploy на Vercel: репозиторий, env‑переменные, preview → ...

Deploy на Vercel: репозиторий, env‑переменные, preview → production

ChatGPT Apps
7 уровень , 3 лекция
Открыта

1. Зачем нам Vercel для ChatGPT App

В прошлых лекциях мы запускали GiftGenius локально и подключали его к ChatGPT через Dev Mode и туннель. Теперь пора сделать ещё один шаг к «взрослому» продакшену и перенести тот же код на Vercel.

К этому моменту у вас уже есть рабочее приложение GiftGenius (наш условный учебный App). Локально оно крутится на Next.js 16 с MCP‑endpoint’ом (например, /api/mcp) и собрано на основе официального ChatGPT Apps SDK Next.js Starter.

Можно было бы пойти путём «я арендую VPS, руками ставлю Node, nginx и сам всё настраиваю», но для Next.js это примерно как писать фронтенд на чистом document.write в 2025 году. Работает, но вы явно усложняете себе жизнь.

Vercel хорош для нас по нескольким причинам.

Во‑первых, он нативно понимает Next.js: автоматически настраивает сборку, SSR, статику, edge‑слой и serverless‑функции. Для ChatGPT App это особенно удобно, потому что виджет и MCP‑endpoint раскатываются одной кнопкой и живут в одной инфраструктуре.

Во‑вторых, Vercel даёт CI/CD из коробки: вы подключаете Git‑репозиторий — и каждый push создаёт новый immutable deployment с уникальным URL. Из ветки main он считается production, из других веток — preview.

В‑третьих, у Vercel приятная история с окружениями и секретами. Он явно делит env‑переменные на Development, Preview и Production, хранит их зашифрованными и позволяет удобно прокидывать в Next.js. Это именно то, что нужно ChatGPT App, где ключи и URL MCP‑сервера должны меняться в зависимости от среды.

В‑четвертых, у Vercel есть удобные роллбэки: если новый релиз пошёл не так, можно быстро промоутить предыдущий успешный деплой и вернуть систему в рабочее состояние. Это снижает «страх деплоя» и поощряет маленькие частые релизы.

И, наконец, Vercel — это компания‑создатель Next.js. Они подогнали Next.js под свои сервера, а свои сервера под Next.js. Используя Vercel вы еще не раз прочувствуйте как плавно и в пару кликов все работает. Горантирую, вам понравится.

2. Исходная точка: структура проекта GiftGenius

По плану курса наш GiftGenius живёт в одном репозитории. Вариантов организации два, и оба допустимы для Vercel:

1) Монорепо с несколькими приложениями — например:

giftgenius/
  apps/
    web/   # Next.js (виджет + MCP)
    mcp/   # отдельный MCP-сервер (если вы его вынесли)

2) Один Next.js‑проект, где и виджет, и MCP живут вместе (это проще на первом этапе и именно так устроен официальный стартер):

giftgenius/
  app/
    page.tsx         # Виджет
    api/
      mcp/route.ts   # MCP endpoint
  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 не попадают в репозиторий. В .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 build, если вы используете pnpm). Это должно собирать проект без ошибок.

В‑третьих, удостоверьтесь, что версия Node указана и подходит для Next.js 16. В release notes Next.js 16 минимальная версия — 18.18.0. Чаще всего достаточно поля в package.json:

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

Vercel подберёт LTS‑версию Node, совместимую с вашим приложением.

Если всё это сделано, можно пушить последний код в 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 (не нужно менять).

Для первого деплоя можно не указывать env‑переменные сразу (мы их добавим отдельным шагом). Нажимаете «Deploy» — Vercel клонирует репозиторий, ставит зависимости, запускает npm run build и, если всё успешно, создаёт первый деплой с адресом вроде https://giftgenius-xyz.vercel.app.

Важно понять одну штуку: каждый деплой — immutable. Если вы потом пушите изменения, создаётся новый деплой с новым URL, а старый остаётся в истории. Продакшен‑домен (например, giftgenius.vercel.app или ваш кастомный домен) указывает на конкретный деплой, и его можно переключить назад, делая rollback.

Схематически это выглядит так:

flowchart LR
    A[GitHub repo
giftgenius] -->|git push| B[Vercel build] B --> C[Preview Deploy #1
unique URL] B --> D[Preview Deploy #2
unique URL] D --> E[Production Alias
giftgenius.vercel.app]

Гит‑ветка main обычно считается production‑веткой, всё остальное — preview. Но это можно перенастроить.

5. Переменные окружения на Vercel

Сейчас ваш первый деплой, скорее всего, не особо работоспособен: нет OPENAI_API_KEY, MCP‑сервер не ходит во внешние API и т.п. Пора заняться env‑переменными.

В Vercel env‑переменные хранятся в разделе Settings → Environment Variables. Там же видно разделение на три scope: Development, Preview и Production.

Табличка для ментальной модели:

Scope Где используется Аналог локально
Development vercel dev и локальный dev через Vercel CLI .env.local
Preview все деплои с веток, кроме production‑ветки staging / test
Production деплои из 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. Настройка env для 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 });
  }
  // Вызов OpenAI или другого сервиса с apiKey...
}

Виджет может использовать APP_BASE_URL на серверной стороне, например, чтобы строить абсолютные ссылки, учитывая ChatGPT‑iframe и конфигурацию assetPrefix/basePath из стартер‑шаблона.

Если ему нужен публичный URL API (например, для window.fetch к вашему backend’у), для этого можно завести NEXT_PUBLIC_API_BASE_URL. Но ни в коем случае не NEXT_PUBLIC_OPENAI_API_KEY.

7. Preview‑деплои: staging на стероидах

Теперь о самом приятном: preview‑деплоях. Когда вы подключили Git‑репозиторий, Vercel автоматически начинает создавать preview‑деплой для каждого пуша в не‑production ветку или для каждого Pull Request. У каждого такого деплоя есть уникальный URL, вроде:

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

Эти деплои используют scope Preview для env‑переменных, так что вы можете задать, например:

# Preview env на Vercel
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, пушите изменения — Vercel даёт preview‑ссылку. Вы идёте в Dev Mode, меняете URL на эту ссылку, проверяете сценарии с GPT (подбор подарка, отображение карточек, вызовы MCP‑инструментов). Только когда всё ок, мёржите в main. Production пока живёт своей спокойной жизнью.

Ментальная схема pipeline:

flowchart TD
    A[Local dev
localhost + туннель] --> B[git push
feature/*] B --> C[Preview Deploy
preview-URL] C --> D[ChatGPT Dev Mode
App → preview-URL] C --> E[Code review / тесты] E --> F[Merge в main] F --> G[Production Deploy
prod-URL] G --> H[ChatGPT Prod App
App → prod-URL]

8. Production‑деплой и rollback

Когда вы мёржите изменения в main (или другую выбранную вами production‑ветку), Vercel создаёт production‑деплой и подвешивает на него production‑alias: giftgenius.vercel.app или ваш собственный домен.

В этот момент ChatGPT Prod‑App (который вы создадите чуть позже) должен быть настроен на production‑URL. В Dev Mode вы продолжаете экспериментировать с туннелем или preview‑URL; обычные пользователи в ChatGPT Store будут ходить именно в production.

Преимущество immutable‑деплоев в том, что rollback становится очень простым. Если новый релиз оказался неудачным (например, MCP‑инструмент падает на боевых данных), не нужно срочно чинить жизнь на проде. Вы открываете список деплоев в Vercel, выбираете предыдущий успешный и нажимаете что‑то вроде «Promote to Production» — K8s и Lambda где‑то далеко перетыкаются, а ваш домен снова указывает на стабильную версию.

В CLI это тоже можно автоматизировать через команды vercel rollback, но на уровне нашего курса достаточно понимать идею: каждый деплой — отдельный артефакт, а production‑alias можно направить на любой из них.

9. Специфика Next.js 16 + MCP на Vercel

С точки зрения Vercel ваш MCP‑endpoint в Next.js — это serverless‑функция (или edge‑функция, если вы так настроили). Она живёт недолго: просыпается по запросу, обрабатывает его и умирает. Состояние между вызовами хранить нельзя, если только вы не используете внешнюю БД или какое‑то хранилище.

Это критично для MCP: если вы вдруг решите складывать историю диалога в глобальный массив let history = [] в route.ts, он будет обнуляться на каждом холодном старте. Для хранения состояния нужно использовать внешнюю систему (KV, Postgres и т.д.), но это уже будущие модули.

Второй аспект — тайм‑ауты выполнения. На бесплатных планах Vercel serverless‑функции имеют ограничение по времени (на момент подготовки материалов — порядка 10 секунд на Hobby, больше на 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, чтобы статика и маршруты корректно работали внутри nested‑iframe’ов web-sandbox.oaiusercontent.com. Благодаря этому все запросы идут на ваш домен, а не на 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).

Для разработки вы продолжаете использовать Dev App, смотрящий на туннель или preview‑URL. Для тестирования дневных/недельных билдов можно завести Staging‑App, привязав его к постоянному preview‑alias. В результате получается трёхступенчатая схема:

Dev App     → локальный туннель или dev-URL (нестабилен)
Staging App → стабильный preview/staging URL на Vercel
Prod App    → production URL на Vercel

Для ориентира сведём всё в одну табличку:

Что URL / деплой на Vercel Scope на Vercel Кто ходит
Dev App локальный туннель / vercel dev Development вы / команда
Staging App стабильный preview‑alias 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), а в проде — третий. Если не вести аккуратный список env‑переменных и не проверять, что в Preview/Production они заполнены корректно, легко получить ситуацию, когда продакшен‑виджет ходит на staging‑бэкенд, а staging‑виджет — на прод. Помогает простая дисциплина: документировать все нужные переменные и проверять их на каждом окружении.

Ошибка №4: игнорирование лимитов по времени выполнения для MCP‑endpoint’ов.
На локалке вы можете ждать ответ от какой‑нибудь медленной внешней системы 30 секунд и не заметить проблем. На Vercel та же функция получит тайм‑аут через 10–15 секунд, и ChatGPT увидит ошибку. Если вы не настроили maxDuration и не следите за временем работы MCP‑инструментов, в проде это может превращаться в случайные падения.

Ошибка №5: попытка хранить состояние MCP в памяти serverless‑функции.
Иногда очень хочется положить историю диалога или кеш рекомендаций в глобальную переменную let cache = {} прямо в файле route handler’а. На локалке, пока dev‑сервер крутится долго, это может даже «работать». Но на Vercel каждая serverless‑функция живёт недолго и часто пересоздаётся. В результате часть запросов «видит» старый cache, часть — новый, а часть — пустой. Это порождает странные баги, которые тяжело воспроизвести. Для состояния нужна внешняя БД или KV‑хранилище; на уровне этой лекции лучше вообще считать MCP‑endpoint stateless.

1
Задача
ChatGPT Apps, 7 уровень, 3 лекция
Недоступна
Health endpoint + бейдж окружения (local / preview / production)
Health endpoint + бейдж окружения (local / preview / production)
1
Задача
ChatGPT Apps, 7 уровень, 3 лекция
Недоступна
Типобезопасная конфигурация env (Zod) с разделением public/private
Типобезопасная конфигурация env (Zod) с разделением public/private
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ