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‑рівень і безсерверні функції. Для ChatGPT App це особливо зручно, адже віджет і MCP‑endpoint розгортаються «в один клік» і працюють в одній інфраструктурі.
По‑друге, Vercel дає CI/CD «з коробки»: ви підʼєднуєте Git‑репозиторій — і кожен push створює нове незмінне (immutable) розгортання з унікальним 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 секрети зберігатимете окремо — в інтерфейсі.
По‑друге, зазирніть у 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. У нотатках до релізу Next.js 16 мінімальна версія — 18.18.0. Найчастіше достатньо поля в package.json:
{
"engines": {
"node": ">=18.18.0"
}
}
Vercel підбере LTS‑версію Node, сумісну з вашим застосунком.
Якщо все це зроблено, можна надіслати (push) останні зміни в Git і переходити до Vercel.
4. Перший імпорт проєкту у Vercel
Тепер переходьте у вебінтерфейс Vercel. Якщо ви там ще не зареєстровані, саме час це зробити.
Ви входите у Vercel, натискаєте «New Project» і обираєте зі списку свій репозиторій giftgenius. На цьому етапі Vercel «за лаштунками» перевіряє вміст репозиторію й майже завжди сам визначає, що це Next.js‑проєкт, підставляючи відповідний пресет.
У налаштуваннях проєкту 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). Якщо ви потім надсилаєте (push) зміни, створюється нове розгортання з новим URL, а старе залишається в історії. Production‑домен (наприклад, giftgenius.vercel.app або ваш кастомний домен) вказує на конкретне розгортання. За потреби його можна перемкнути назад, зробивши rollback.
Схематично це виглядає так:
flowchart LR
A[GitHub‑репозиторій
giftgenius] -->|git push| B[Збирання Vercel]
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 тощо. Саме час зайнятися 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 до вашого бекенду), для цього можна завести NEXT_PUBLIC_API_BASE_URL. Але в жодному разі не NEXT_PUBLIC_OPENAI_API_KEY.
7. Preview‑розгортання: staging без зайвої метушні
Тепер про найприємніше — preview‑розгортання. Коли ви підʼєднали Git‑репозиторій, Vercel автоматично починає створювати preview‑розгортання для кожного push у будь‑яку гілку, крім production, або для кожного Pull Request. У кожного такого розгортання є унікальний URL, на кшталт:
https://giftgenius-git-feature-new-layout-username.vercel.app
Ці розгортання використовують scope Preview для env‑змінних, тож ви можете задати, наприклад:
# Середовище Preview на 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, надсилаєте (push) зміни — Vercel дає preview‑посилання. Далі ви заходите в Dev Mode, змінюєте URL на це посилання, перевіряєте сценарії з GPT (підбір подарунка, відображення карток, виклики MCP‑інструментів). І лише коли все гаразд, зливаєте зміни в main. Production тим часом живе своїм спокійним життям.
Ментальна схема pipeline:
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[Merge у main]
F --> G[Production‑розгортання
prod‑URL]
G --> H[ChatGPT Prod App
App → prod‑URL]
8. Production‑розгортання і rollback
Коли ви зливаєте зміни в main (або в іншу обрану вами production‑гілку), Vercel створює production‑розгортання і «вішає» на нього production‑аліас: 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‑аліас можна спрямувати на будь‑яке з них.
9. Специфіка Next.js 16 + MCP на Vercel
З погляду Vercel ваш MCP‑endpoint у Next.js — це безсерверна функція (або edge‑функція, якщо ви так налаштували). Вона живе недовго: «прокидається» за запитом, обробляє його і «помирає». Стан між викликами зберігати не можна, хіба що ви використовуєте зовнішню БД або якесь сховище.
Це критично для MCP. Якщо ви раптом вирішите складати історію діалогу в глобальний масив let history = [] у route.ts, він обнулятиметься на кожному холодному старті. Щоб зберігати стан, потрібна зовнішня система (KV, Postgres тощо), але це вже тема майбутніх модулів.
Другий аспект — тайм‑аути виконання. На безплатних планах Vercel безсерверні функції мають обмеження за часом (на момент підготовки матеріалів — приблизно 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, щоб статика й маршрути коректно працювали всередині вкладених 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‑аліаса. У результаті виходить триступенева схема:
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‑аліас | 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 у памʼяті безсерверної функції.
Інколи дуже хочеться покласти історію діалогу або кеш рекомендацій у глобальну змінну let cache = {} просто у файлі обробника маршруту. Локально, доки dev‑сервер довго працює, це може навіть «працювати». Але на Vercel кожна безсерверна функція живе недовго й часто створюється заново. У результаті частина запитів «бачить» старий cache, частина — новий, а частина — порожній. Це породжує дивні баги, які важко відтворити. Для стану потрібна зовнішня БД або KV‑сховище; у межах цієї лекції краще взагалі вважати MCP‑endpoint stateless.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ