API Playground
packages/api-playground — це Vue-застосунок для розробників: він завантажує опис маршрутів із backend і дає змогу вручну викликати HTTP- та WebSocket-маршрути. Це інструмент лише для локальної розробки. У production він не потрібен і не призначений для нього.
Як backend віддає опис маршрутів і коли endpoint /doc/routes взагалі доступний, описано в розділі Маршрутизація і Middleware. Тут розглянуто саму клієнтську частину.
Стек
За даними package.json:
- Vue 3,
vue-router, Pinia,@vueuse/core; - Vite 7 з
@vitejs/plugin-vueіvite-plugin-vue-devtools; - Tailwind CSS 3 (PostCSS, autoprefixer);
- TypeScript
6.0.3, перевірка типів черезvue-tsc; - workspace-залежність
shared.
Скрипти: dev, build (type-check разом із vite build), preview, typecheck, lint, lint:check, format. Скрипта test немає.
Запуск
- У
packages/backend/.envзадайтеAPP_ENV=manual-test,DOC_PAGE=true,SERVE_STATIC=true. Пояснення до цих змінних є в розділах Маршрутизація і Конфігурація. - З кореня репозиторію виконайте
pnpm dev:manual_test. Команда збираєshared, а потім паралельно запускає backend і playground (кореневийpackage.json). Інші скрипти описано в довіднику скриптів. - Відкрийте
http://localhost:5174.
Vite proxy
У vite.config.ts dev-сервер слухає порт 5174 і проксіює запити:
| Префікс | Ціль | Примітка |
|---|---|---|
/<VITE_API_PATH_PREFIX> (за замовчуванням /api) | http://127.0.0.1:3000 | changeOrigin: true, ws: true — HTTP і WS-ендпоінт /<prefix>/websocket |
Префікс API береться з VITE_API_PATH_PREFIX (має збігатися з бекендовим API_PATH_PREFIX, за замовчуванням api). WS-відповідь розпізнається за очікуваним запитом з тим самим event, тож працює з будь-яким префіксом. Базовий URL для HTTP-викликів за замовчуванням порожній. Тому запити йдуть відносно origin dev-сервера і проходять через proxy. Його можна перевизначити змінною VITE_API_BASE_URL або в налаштуваннях UI (stores/api-settings.ts).
Завантаження опису маршрутів
Функція fetchRoutes() у stores/api-doc.ts робить fetch(apiUrl(baseUrl, 'doc/routes')) (utils/api-path.ts): <baseUrl>/<VITE_API_PATH_PREFIX>/doc/routes, з порожнім baseUrl — через proxy. Відповідь має форму { httpRoutes, wsRoutes, pathPrefix }. Store об'єднує групи з однаковим префіксом, присвоює маршрутам числові id і будує для навігації плоскі списки з fullUrl (префікс + шляхи груп + url). Отриманий pathPrefix зберігається в налаштуваннях.
Серіалізацію на backend виконує serialize-routes.ts. Вона копіює поля маршруту, замінює handler на null і прибирає validator, queryValidator та ResponseSchema. Замість них додаються:
inputSchema— зvalidator(тіло запиту);querySchema— зqueryValidator;outputSchema— зResponseSchema;bodyLimits— лише дляpost/put/patch: список{ kind, maxBytes }для кожного типу зallowedContentTypes(за замовчуванням["json"]), бо ліміт залежить відContent-Typeзапиту.[]означаєallowedContentTypes: [], і картка пише «No allowed body types». Обчислюється черезresolveMaxBodySizeу serialize-routes.ts.
Кожна схема має форму { expression, fields[], jsonSchema? } (тип ArkSchema у store). Решта полів, які використовує UI: url, method, description, middlewares, rateLimit / groupRateLimit ({ windowMs, maxRequests }), а для груп ще prefix і group. Детальніше про схеми див. Валідація.
Картка маршруту (ApiRoute.vue) показує метод, URL, middleware, схеми й rate limit. Якщо в маршруту немає власного ліміту, показується ліміт групи з позначкою route/group. Є також окрема сторінка маршруту /route/:routeId (router/index.ts).
Будова інтерфейсу
Головна сторінка (ApiHomeView.vue) має три колонки під шапкою ApiHeader:
| Колонка | Компонент | Що показує |
|---|---|---|
ліва (від ширини xl) | SiteNavigation.vue | дерево груп і маршрутів (TreeGroup.vue), перемикач HTTP / WebSocket |
| центральна | ApiGroup.vue → ApiRoute.vue | групи та картки маршрутів |
права (від xl) | OnThisPage.vue | зміст сторінки з підсвіткою активного розділу |
На вужчих екранах навігацію дає MobileNavigation.vue (з MobileGroupItem.vue). RouteGroupsMenu.vue — меню груп. Окремий маршрут відкривається на /route/:routeId (RouteDetailView.vue).
Що реалізовано
HTTP-запити
Форма TestForm.vue містить:
- поля для path-параметрів: кожен
:nameуfullUrlзамінюється введеним значенням; - заголовки у форматі JSON (за замовчуванням
Content-Type: application/json) з перевіркою JSON; - тіло у форматі JSON, яке заповнюється заготовкою з
inputSchema. ДляGETіDELETEтіло не надсилається (utils/base-api.ts); - генерацію еквівалентної
curl-команди; - повторні запуски: кількість запитів і кількість «потоків», зі статистикою часу відповіді (середній, мінімальний, максимальний) і кількістю успішних запитів. Запуск виконує composable
useLoadTest(типи й підрахунки — уutils/load-test.ts); прокрутку до відповіді й форматування тілаTestForm.vueділить ізTestFormWs.vueчерезutils/test-form-helpers.ts; - перегляд відповіді: статус, заголовки, час, тіло.
Окремого редактора query-рядка немає. querySchema лише відображається в картці маршруту, тому query-параметри доведеться дописати в URL вручну.
Глобальні налаштування
ApiSettingsModal.vue і stores/api-settings.ts керують такими налаштуваннями:
baseUrl, який зберігається вlocalStorage, і тимчасовий override на поточну сесію (він уlocalStorageне потрапляє);- глобальні заголовки з перемикачем
enableGlobalHeaders, які додаються до кожного HTTP-запиту. Значення заголовків (Authorization,Cookie, ключі API — це облікові дані, які розробник вводить, щоб перевірити API) зберігаються лише вsessionStorage(живуть одну вкладку), а не вlocalStorage, де вони пережили б вкладку і були б доступні кожному скрипту origin. Базова адреса, префікс і перемикач лишаються вlocalStorage. Значення, збережені вlocalStorageстарою версією, ігноруються і стираються. У консолі журнал запиту показує[redacted]замість значень чутливих заголовків (authorization,cookie,*token*,*api-key*,*secret*);console.logвирізається з production-збірки, як і у фронтенді; - скидання до значень за замовчуванням.
Окремих «середовищ» зі змінними немає. Тема (light/dark) теж зберігається в localStorage (useTheme.ts).
Авторизація та сесія
Спеціальної логіки для автентифікації чи CSRF немає: CSRF-токен автоматично не отримується і не підставляється. fetch викликається без явного credentials. Через Vite proxy запити same-origin, тому браузер надсилає cookie за звичайними правилами. Потрібні заголовки (наприклад, токен) можна додати вручну в заголовки запиту або в глобальні заголовки. Для POST/PUT/PATCH/DELETE потрібні дві речі: заголовок x-csrf-token (значення csrfToken з відповіді GET /api/main/init) і дозволений origin. Браузер надсилає Origin: http://localhost:5174, а в dev бекенд за замовчуванням довіряє лише origin з APP_URL і FRONTEND_URL. Тому додайте playground у packages/backend/.env: CSRF_ALLOWED_ORIGINS=http://localhost:5173,http://localhost:5174, і перезапустіть бекенд (CSRF простими словами). Про механізми захисту на backend див. Безпека.
WebSocket
useWebSocketConnection.ts разом із TestFormWs.vue і WebSocketModal.vue підтримують:
- підключення за URL і токеном. Токен передається як subprotocol (
Sec-WebSocket-Protocol); - автозаповнення: якщо HTTP-відповідь містить
wsUrlіwsToken, вони підставляються у форму підключення; - надсилання повідомлень у форматі
{ event, timestamp, payload }, деevent— повний шлях WS-маршруту. Відповідь зіставляється з запитом заevent, діє тайм-аут. Паралельний запит з тим самимeventвідхиляється; - обробку закриття з'єднання: код
4001вважається неавторизованим доступом, для кодів4000–4099перепідключення не виконується.
Протокол описано в розділі WebSocket.
Чого немає
Історії запитів і збережених запитів немає: стан форми ніде не зберігається.
Типові проблеми
| Симптом | Що перевірити |
|---|---|
маршрути не завантажуються, Failed to fetch або 404 на /api/doc/routes | backend запущений на :3000; у packages/backend/.env задано APP_ENV=manual-test, DOC_PAGE=true, SERVE_STATIC=true (без усіх трьох endpoint не реєструється); API_PATH_PREFIX — api (див. «Обмеження») |
| HTTP-запити йдуть не туди | VITE_API_BASE_URL і baseUrl / override у налаштуваннях: порожнє значення означає «через Vite proxy» |
| WebSocket не підключається | URL і токен у формі підключення; токен береться з відповіді, що містить wsUrl і wsToken; закриття з кодом 4001 — токен недійсний |
| помилки CORS у консолі | запити йдуть повз proxy (заданий baseUrl на інший origin); тоді origin playground має бути в CORS-whitelist backend (Безпека) |
POST повертає 403 з code: csrf_invalid | reason: origin_mismatch — origin playground (http://localhost:5174) немає в CSRF_ALLOWED_ORIGINS; reason: token_mismatch — немає або застарів x-csrf-token (візьміть новий з GET /api/main/init) |
Кнопка Test Connection у налаштуваннях робить GET <baseUrl>/<prefix>/ping — liveness-маршрут бекенду (раніше вона била в неіснуючий /health). WS-адреса за замовчуванням виводиться з baseUrl: http:// → ws://, https:// → wss://. Повідомлення не ставляться в чергу: без відкритого з'єднання sendMessage нічого не надсилає, пише помилку в консоль і повертає false.
Обмеження
- Тести — Vitest (
pnpm --filter api-playground test): побудова URL під префіксом,http/https→ws/wss,sendMessageбез з'єднання. - Скрипт
buildіснує (vite build), але production-розгортання playground у репозиторії не налаштоване. Застосунок розрахований на dev-сервер із proxy. - У
packages/api-playground/docs/лишились дві внутрішні нотатки —ROADMAP.mdіUI-UX-ANALYSIS.md. Це ідеї та аналіз, а не опис фактичної поведінки.
Безпека
Ніколи не вмикайте APP_ENV=manual-test і DOC_PAGE=true у production. /doc/routes розкриває повну карту API: URL, middleware, схеми валідації та rate limits. Див. Безпека і Маршрутизація.