JavaRush /Курси /ChatGPT Apps /Тунель по‑дорослому: стабільні dev‑URL та оновлення в Dev...

Тунель по‑дорослому: стабільні dev‑URL та оновлення в Dev Mode

ChatGPT Apps
Рівень 7 , Лекція 2
Відкрита

1. Проблема «випадкових» тунелів

Коли ви вперше запускаєте ngrok http 3000 або швидкий Cloudflare Quick Tunnel, усе здається магією: раз — і ваш http://localhost:3000 перетворюється на https://random-1234.tunnelprovider.com. Ви копіюєте URL у ChatGPT Dev Mode — і GPT охоче завантажує ваш застосунок.

А потім ви перезапускаєте тунель… і отримуєте новий домен. Старий URL у налаштуваннях dev‑застосунку в ChatGPT раптом стає «битим посиланням». GPT чесно пише «App unavailable», а ви знову йдете в налаштування, змінюєте URL, тиснете Save, чекаєте, доки все оновиться, — і щоразу дратуєтеся через усю цю купу кроків.

Для разового «погратися ввечері» це ще терпимо. Але якщо ви:

  • щодня допрацьовуєте застосунок;
  • хочете показати проміжну версію колезі або менеджеру;
  • паралельно налаштовуєте staging і production,

то перепідʼєднувати Dev Mode під кожен новий випадковий URL швидко перетворюється на суцільні муки.

До того ж, якщо ви вже додали в застосунок щось, що залежить від домену (наприклад, redirect URI для OAuth або вебхуки), кожен новий URL ламає і це. Виникає каскад: змінили тунель — і доводиться правити конфігурацію застосунку, redirect URI в OAuth‑провайдері та налаштування обробника вебхуків.

Звідси й ключова ідея лекції: стабільний dev‑URL — не розкіш, а спосіб берегти психічне здоровʼя розробника.

Інсайт

У ChatGPT діють доволі суворі часові обмеження на роботу з вашим застосунком, і їх легко недооцінити. Для MCP‑tool‑call є ліміт: максимум 2 хвилини. Після цього платформа вважає виклик невдалим — навіть якщо ваш сервер і далі щось робить.

Ще суворіші обмеження діють під час реєстрації застосунку (Store або Dev Mode): на читання маніфесту, ресурсів і описів інструментів ChatGPT дає близько 20 секунд. Якщо за цей час ваш MCP‑сервер не встиг ініціалізуватися й повернути список tools/resources тощо, реєстрація застосунку завершиться тайм‑аутом.

Рекомендація: усю важку ініціалізацію виконайте до того, як іти в Dev Mode або Store. Прогрівання зʼєднань із БД, завантаження великих конфігів, «ледачі» кеші — усе це краще зробити заздалегідь. Наприклад, один раз звернутися до сервера через MCP Jam або запустити окремий внутрішній скрипт. Для платформи MCP‑сервер має бути «теплим» і відповідати за секунди, а не «прокидатися» під час реєстрації.

2. Що таке «дорослий» тунель

Зафіксуймо, чим «дорослий» тунель відрізняється від того, що ви запускали на початку курсу.

Ранній підхід (модуль 2) виглядав так:

# Приклад на ngrok
ngrok http 3000
# Отримуємо: https://random-abc123.ngrok-free.app

Ви брали цей одноразовий URL і вставляли в Dev Mode. Під час наступного запуску ngrok URL уже інший — і конфігурація ChatGPT застаріває.

У «дорослому» підході у вас:

  • є статичний піддомен у тунель‑провайдера (або власний домен);
  • один і той самий домен завжди проксіюється на ваш localhost:3000;
  • ви можете перезапускати тунель, машину й роутер, але URL залишатиметься тим самим.

Такі статичні піддомени доступні, наприклад:

  • у ngrok — один безкоштовний статичний домен на обліковий запис;
  • у Cloudflare Tunnel — через іменований тунель і привʼязку до власного домену.

І ChatGPT‑застосунок у Dev Mode налаштований рівно на цей один URL — і більше вам не доводиться про це думати.

З формального погляду вимоги до нашого «дорослого» тунелю такі:

  • стабільно один і той самий публічний HTTPS‑домен;
  • дійсний TLS‑сертифікат (провайдер забезпечує це за нас);
  • конфігурація, яка описує: «усе, що прийшло на https://dev.yourdomain.com, спрямуй на http://localhost:3000»;
  • за потреби — мінімальні заходи безпеки (хоча б не світити URL на Stack Overflow).

3. Налаштовуємо стабільний dev‑URL: приклад із Cloudflare Tunnel

У курсі ми рекомендуємо Cloudflare Tunnel як основний інструмент, тому що він добре підходить і для dev, і для складніших сценаріїв. У модулі 2 ви вже бачили приклад базового налаштування — тепер «доведемо» його до постійного dev‑URL.

Припустімо, у нас навчальний застосунок GiftGenius, і ми хочемо стабільний URL giftgenius-dev.yourdomain.com.

Мінімальні кроки (спрощено, без привʼязки до UI Cloudflare):

  1. Привʼязуємо домен до акаунта Cloudflare (один раз, через їхню панель).
  2. Встановлюємо cloudflared локально та входимо в обліковий запис.
brew install cloudflare/cloudflare/cloudflared   # macOS
cloudflared login                                # відкриє браузер для авторизації

Крок 3. Створюємо іменований тунель:

cloudflared tunnel create giftgenius-dev

Крок 4. Налаштовуємо маршрут у ~/.cloudflared/config.yml:

tunnel: giftgenius-dev
credentials-file: /Users/you/.cloudflared/giftgenius-dev.json

ingress:
  - hostname: giftgenius-dev.yourdomain.com
    service: http://localhost:3000  # наш Next.js dev-сервер
  - service: http_status:404

Крок 5. Запускаємо тунель:

cloudflared tunnel run giftgenius-dev

Тепер, доки працюють npm run dev і cloudflared tunnel run, ваш локальний Next.js доступний за постійним URL https://giftgenius-dev.yourdomain.com. Саме його ви зазначаєте в налаштуваннях ChatGPT Dev Mode.

Як це повʼязується з нашим застосунком

Якщо ви відкриєте в браузері URL застосунку, який вводите в ChatGPT під час підключення dev‑застосунку:

https://giftgenius-dev.yourdomain.com/mcp

ви побачите відповідь (помилку) — щось на кшталт:

{"jsonrpc":"2.0","error":{"code":-32000,"message":"Method not allowed."},"id":null}

Це абсолютно нормально, адже сервер на /mcp не очікує GET-запиту. Усі інші частини застосунку — віджет, MCP‑ендпойнт /mcp, API‑роути — працюють через цей самий тунель. Вам не потрібно щоразу запамʼятовувати новий домен.

4. Альтернатива: стабільний піддомен у ngrok

Якщо ви вже звикли до ngrok, його можна «зробити дорослим» приблизно так само — використовуючи статичний домен. Із 2023 року ngrok навіть на безкоштовному плані дає змогу закріпити один статичний піддомен на кшталт myapp-dev.ngrok-free.app.

Мінімальна схема:

# ~/.config/ngrok/ngrok.yml
authtoken: <ваш токен>
tunnels:
  giftgenius-dev:
    addr: 3000
    proto: http
    domain: giftgenius-dev.ngrok-free.app

Запуск:

ngrok start giftgenius-dev

У результаті URL https://giftgenius-dev.ngrok-free.app буде постійним — і саме його ви даєте ChatGPT Dev Mode як базовий URL застосунку.

Філософія та сама:

  • жодних «випадкових» адрес;
  • змінюється лише внутрішній стан тунелю (запущено/не запущено), а не домен;
  • Dev Mode не потрібно перепідʼєднувати.

Cloudflare і ngrok у цьому сенсі — просто різні «смаки морозива». Дехто любить власні домени та «тонкий» контроль DNS (Cloudflare), інші надають перевагу підходу «написали YAML — і готово» (ngrok). Для курсу обидва варіанти підходять; головне — стабільний URL.

5. Схема: ChatGPT Dev Mode ↔ тунель ↔ локальний стек

Щоб трохи формалізувати те, що відбувається, намалюємо схему.

flowchart TD
    ChatGPT["ChatGPT (Dev Mode)"]
    AppCfg["Dev App (конфіг: httрs://giftgenius-dev...)"]
    Tunnel["Тунель Cloudflare/ngrok (giftgenius-dev...)"]
    Next["Next.js dev-сервер localhost:3000 + MCP обробник"]

    ChatGPT --> AppCfg
    AppCfg -->|"у конфігурації вказано https://giftgenius-dev.../.well-known/openai-app"| Tunnel
    Tunnel -->|"HTTPS → HTTP проксіювання"| Next

ChatGPT ніколи не знає, що ви там робите на ноутбуці. Для нього існує лише один HTTPS‑ендпойнт. Що саме за ним — Vercel, локальний тунель, Kubernetes — уже ваша справа. А в цій лекції нас цікавить саме стабільність цього HTTPS‑ендпойнта для локальної розробки.

Залишилося зробити так, щоб усередині застосунку ця адреса теж була єдиним «джерелом правди», а не розʼїжджалася по жорстко прописаних рядках. Саме цьому й присвячено наступний розділ.

6. Змінні середовища та baseUrl у коді

Щоб усе працювало без сюрпризів, корисно один раз визначити в коді Next.js «базовий зовнішній URL застосунку» — і далі спиратися лише на нього.

Наприклад, у папці app/lib/config.ts у нашому застосунку GiftGenius можна завести:

// app/lib/config.ts
export const baseUrl =
  process.env.NEXT_PUBLIC_APP_URL ?? "http://localhost:3000"; // fallback

export const mcpEndpoint = `${baseUrl}/mcp`;  // URL MCP-сервера

А в .env.local під час розробки вказати:

NEXT_PUBLIC_APP_URL=https://giftgenius-dev.yourdomain.com

Тоді:

  • усередині віджета та будь‑яких посилань ви завжди використовуєте baseUrl;
  • для ChatGPT Dev Mode і браузера все виглядає послідовно;
  • якщо завтра ви переїдете на Vercel staging із доменом https://giftgenius-staging.vercel.app, достатньо змінити лише змінну середовища.

Це особливо важливо для:

  • callback‑URL (наприклад, для OAuth і webhook‑обробників);
  • посилань, які ви показуєте користувачеві у віджеті (кнопка «Відкрити в браузері» через openExternal);
  • будь‑яких абсолютних URL у логіці застосунку.

Поки ми говоримо лише про dev‑URL, але сама архітектурна ідея «одне джерело правди для baseUrl» чудово працює й так само спокійно переїде з вами на staging/production.

7. Оновлення URL у ChatGPT Dev Mode

Гаразд, ми зробили собі стабільний домен. Як тепер із ним жити в Dev Mode?

Логіка така:

  1. У налаштуваннях dev‑застосунку ви один раз вказуєте кореневий URL: https://giftgenius-dev.yourdomain.com/
  2. ChatGPT звертається до нього по маніфест (.well-known/openai-app) і далі використовує той самий корінь для звернення до MCP (/mcp), статичних файлів тощо.
  3. Якщо ви змінюєте лише код (React‑віджет, MCP‑обробники, стилі), URL змінювати взагалі не потрібно. Достатньо, щоб тунель був запущений і Next.js‑сервер відповідав.
  4. Якщо ви змінюєте сам домен (рідко — наприклад, переходите з ngrok на Cloudflare), потрібно один раз зайти в Dev Mode і змінити ендпойнт.

У деяких випадках ChatGPT кешує маніфест, і зміни в ньому можуть зʼявлятися не миттєво. В інтерфейсі Dev Mode зазвичай є кнопка на кшталт «Reload configuration / Refresh App», але в найгіршому сценарії допоможе навіть просте «відʼєднати й знову підʼєднати застосунок за тим самим URL».

Важливо: доки ви не змінюєте URL, Dev Mode автоматично «підхоплює» нові версії коду. Основний тригер для застосунку — домен, а не хеш коміту.

8. Перемикання між dev / staging / prod у Dev Mode

Стабільний dev‑домен — це лише перша сходинка. Щоб не загубитися в хаосі URL‑адрес у міру зростання проєкту, корисно відразу розуміти, як dev‑тунель вписується в загальну схему середовищ (dev/staging/prod) і Dev Mode. Хоча staging і prod — тема радше наступної лекції про Vercel, Dev Mode вже зараз уміє працювати з кількома середовищами.

Щоб легше було стежити за подальшими міркуваннями, корисна така таблиця:

Середовище Базовий URL Де працює код
Local
https://giftgenius-dev.yourdomain.com
Локальний Next.js + MCP через тунель
Staging
https://giftgenius-staging.vercel.app
Vercel Preview / staging‑деплой
Prod
https://giftgenius.vercel.app
Vercel Production

Варіантів роботи з Dev Mode — два.

Перший: один dev‑застосунок, але ви час від часу оновлюєте в його налаштуваннях URL, щоб перевірити staging або prod (обережно). Такий підхід годиться на ранніх стадіях, але його легко переплутати: сьогодні ви тестували локальне середовище, завтра — staging, післязавтра забули перемкнутися — і випадково надсилаєте через dev‑застосунок запити в prod.

Другий — здоровіший: кілька dev‑застосунків, кожен із чіткою привʼязкою до середовища:

  • GiftGenius Devgiftgenius-dev.yourdomain.com;
  • GiftGenius Staginggiftgenius-staging.vercel.app;
  • GiftGenius (бойовий, через Store) → giftgenius.vercel.app.

У межах цієї лекції ми крок за кроком наводимо лад хоча б у dev‑URL. У наступній лекції ви побачите, як логічно привʼязати Vercel і preview‑деплої до staging/production.

9. Робота в команді: кілька розробників і один тунель

Поки у вас один dev‑домен і один розробник, тунель — ваш особистий інструмент. Але щойно в проєкті зʼявляється команда, тунелі та середовища починають перетинатися. Тут важливо не влаштувати «війну за один піддомен».

Уявімо, що два розробники вирішили використовувати один і той самий статичний піддомен, скажімо giftgenius-dev.ngrok-free.app. Обидва запускають у себе ngrok start giftgenius-dev. У кращому разі один тунель просто не підніметься (конфлікт домену), у гіршому — ви будете по черзі «перебивати» одне одному сесію, і ChatGPT то потраплятиме до одного, то до іншого.

Є кілька стратегій.

Найпростіша — персональні dev‑домени:

  • alex.dev.giftgenius.app;
  • maria.dev.giftgenius.app.

І кожному — свій dev‑застосунок у ChatGPT, наприклад GiftGenius Dev (Alex) і GiftGenius Dev (Maria). Тоді кожен спокійно працює локально й не заважає колезі.

Більш «командний» шлях — спільний staging‑ендпойнт:

  • У всіх розробників є особистий dev‑тунель (для персонального налагодження).
  • Плюс є staging на Vercel, куди зливаються feature‑гілки і куди дивиться спільний dev‑застосунок GiftGenius Staging.

Такий підхід часто трапляється в реальних командах:

  • функціональність народжується локально й налагоджується через особистий тунель;
  • після pull‑request і злиття вона тестується всіма через staging (без тунелів — просто через Vercel URL).

10. Безпека dev‑тунелю (коротко й без параної)

Тунель — це зручний спосіб вивести ваш локальний сервер в інтернет. А інтернет, як ми знаємо, здебільшого складається з ботів, сканерів і людей, які люблять перевіряти, чи не забули ви пароль admin/admin.

Базові речі, про які варто памʼятати вже на етапі dev:

  • тунель дає зовнішній доступ до всього, що висить на цьому порту; не виносьте туди ще й адмінку БД, phpMyAdmin і «мою тестову CRM без пароля»;
  • не публікуйте URL тунелю у відкритих репозиторіях і чатах;
  • вимикайте тунель, коли не працюєте (і ноутбук також іноді вимикайте — йому теж потрібен відпочинок).

Більш серйозні заходи на кшталт Basic Auth, перевірки спецзаголовків або токена в URL ми розбиратимемо вже в модулях про безпеку. Зараз важливо лише одне: тунель — інструмент розробки, а не захищений сервер. Бойове середовище житиме на нормальному хостингу, на кшталт Vercel, з іншими механізмами захисту.

11. Практика: налаштовуємо стабільний dev‑URL для нашого застосунку

Тепер від теорії та застережень — до практики: давайте привʼяжемо все це до нашого навчального застосунку на Next.js (шаблон Apps SDK).

Припустімо, структура проєкту виглядає так:

apps/
  web/          # Next.js App + віджет
  mcp-server/   # (необов’язково) окремий MCP, або /mcp-обробник у web

На практиці ви можете тримати MCP прямо в Next.js — у app/api/mcp/route.ts, але принцип той самий.

Крок 1. Правимо .env.local

Додайте туди стабільний dev‑URL тунелю:

NEXT_PUBLIC_APP_URL=https://giftgenius-dev.yourdomain.com

У dev‑коді ми вже використовували baseUrl із цієї змінної (див. вище). Якщо ви цього ще не зробили — саме час винести й підʼєднати.

Крок 2. Запускаємо dev‑сервер і тунель

cd apps/web
npm run dev          # Next.js на localhost:3000

# окремий термінал
cloudflared tunnel run giftgenius-dev

Перевірте в браузері, що https://giftgenius-dev.yourdomain.com відкривається й показує ваш застосунок.

Крок 3. Підключаємо в ChatGPT Dev Mode

В інтерфейсі ChatGPT (секція для розробників):

  • створюємо або редагуємо GiftGenius Dev;
  • у полі URL/Endpoint вказуємо https://giftgenius-dev.yourdomain.com/;
  • зберігаємо.

Після цього ChatGPT звертається до маніфесту за шляхом /.well-known/openai-app, а потім починає запускати ваш застосунок поверх цього домену.

Тепер ви можете:

  • міняти код віджета, MCP‑обробники, стилі;
  • перезапускати npm run dev;
  • перезапускати cloudflared tunnel run giftgenius-dev;

і при цьому більше ніколи не заходити в налаштування dev‑застосунку, доки домен залишається тим самим.

12. Як це виглядає в логіці коду: приклад із openExternal

Щоб завершити приклад тим, що ми вже писали в попередніх лекціях, додамо у віджет кнопку «Відкрити повний інтерфейс у браузері». Вона також використовуватиме стабільний dev‑URL.

Припустімо, у нас є React‑компонент віджета GiftWidget:

// app/components/GiftWidget.tsx
"use client";

import { baseUrl } from "../lib/config"; // беремо baseUrl з env

export function GiftWidget() {
  const handleOpenFull = () => {
    window.openai.openExternal({
      // відкриваємо сторінку застосунку в окремій вкладці
      url: `${baseUrl}/full`,
      label: "Відкрити повний інтерфейс",
    });
  };

  return (
    <div>
      <button onClick={handleOpenFull}>
        Повний режим
      </button>
    </div>
  );
}

Якщо NEXT_PUBLIC_APP_URL вказує на тунель, то:

  • під час локальної розробки відкриється сторінка https://giftgenius-dev.yourdomain.com/full;
  • після деплою на staging — https://giftgenius-staging.vercel.app/full;
  • у prod — бойовий домен.

І знову — одне джерело правди для домену: змінюємо середовище, але не змінюємо код.

13. Міні‑стратегія: як думати про тунель «по‑дорослому»

Якщо звести все до простої ментальної моделі, то можна вважати, що:

  • тунель — це просто тимчасовий провід між вашим ноутбуком і стабільним публічним доменом;
  • ChatGPT Dev Mode знає лише домен — і йому байдуже, де фізично працює код;
  • що рідше ви змінюєте домен, то менше часу проводите в налаштуваннях ChatGPT і OAuth‑провайдерів;
  • dev‑тунель — це лише один рядок у вашій загальній карті середовищ, поруч зі staging (Vercel preview) і prod (Vercel production).

Наступна лекція якраз покаже, як цей «провід» замінюється повноцінним хостингом на Vercel і як повʼязати все це з Git‑гілками, preview‑деплоями та production.

14. Типові помилки під час роботи з «дорослим» тунелем

Помилка № 1: «Я налаштував статичний домен, але все одно продовжую використовувати випадкові URL».
Іноді розробник один раз робить красивий giftgenius-dev.yourdomain.com, але за звичкою запускає ngrok http 3000 без конфігурації. У результаті ChatGPT дивиться на один домен, а код працює через інший. Якщо ви вже зробили стабільний dev‑URL — використовуйте лише його й запускайте тунель через конфігурацію (іменований тунель або профіль).

Помилка № 2: Жорстко прописаний localhost:3000 у коді.
Це трапляється, коли в React‑компоненті або MCP‑обробнику пишуть fetch("http://localhost:3000/api/..."). Локально ще якось працює, але в Dev Mode і тим більше на staging/prod ламається миттєво. Завжди виносьте базовий URL у конфіг (baseUrl, NEXT_PUBLIC_APP_URL) і використовуйте його всюди, де потрібні абсолютні посилання.

Помилка № 3: Постійна правка URL у Dev Mode замість стабільного тунелю.
Якщо ви ловите себе на думці «ну гаразд, іще раз поміняю URL у налаштуваннях — що вже там», — це сигнал. Налаштування статичного піддомену в ngrok або Cloudflare займає один раз 10–15 хвилин, зате економить години під час розробки.

Помилка № 4: Спільний статичний домен на команду без правил.
Два розробники, один домен giftgenius-dev.ngrok-free.app, і обидва запускають тунель, коли заманеться. Результат — конфлікт тунелів, «містично» зникаючі відповіді в Dev Mode і налагодження в стилі «у мене ж працювало». Для команди завжди або особисті dev‑домени, або один staging‑домен на реальному хостингу.

Помилка № 5: Тунель як «майже продакшн».
Іноді хтось вирішує: «Ну якщо в мене стабільний HTTPS‑URL через тунель, давайте пускати через нього справжніх користувачів або платежі». Це шлях до болю: вимкнули ноутбук — застосунок «упав», зник інтернет — те саме, а безпека в кращому разі символічна. Тунель — це dev‑інструмент. Для production‑трафіку призначені Vercel та інша доросла інфраструктура, до якої ми дістанемося в наступній лекції.

Помилка № 6: Забута синхронізація змін середовища і Dev Mode.
Часто змінюють NEXT_PUBLIC_APP_URL у .env.local, але забувають змінити URL у Dev Mode (або навпаки). У результаті віджет генерує посилання на один домен, а ChatGPT звертається до іншого. Ведіть просту табличку «середовище ↔ домен ↔ застосунок у ChatGPT» і оновлюйте її під час змін — це дешевше, ніж гадати, який URL зараз справжній.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ