При розробці headless-проектів на Directus стандартна генерація API часто не покриває бізнес-логіку. Типова ситуація: фронтенд робить 30 запитів для сторінки каталогу через N+1 реляцій, фільтри не працюють для вкладених полів, а GraphQL видає помилки при складних агрегаціях. Замовник втрачає користувачів через повільне завантаження. Ми — команда з п'ятирічним досвідом, сертифіковані партнери Directus з понад 50 успішними проектами — перетворюємо API на продуктивне рішення. Наші клієнти економлять до 40% серверних ресурсів і скорочують TTFB з 2 секунд до 200 мс. Середня економія — $5000 на рік на інфраструктурі.
Проблеми, які вирішуємо
N+1 запитів при роботі з реляціями
Стандартний REST-запит /items/articles повертає лише flat-поля. Якщо фронтенду потрібні автор, категорія та коментарі — він робить 3 додаткових запити на кожен пост. При 20 постах на сторінці це 60 запитів. Рішення — використовувати fields=*,author.*,category.*,comments.* з одним запитом. Але якщо потрібно сортувати коментарі або лімітувати їх кількість — потрібен deep-параметр. Ми налаштовуємо глибокі populate з кастомними сортуваннями та лімітами, знижуючи кількість запитів до 1–2.
Фільтрація за пов'язаними колекціями
Часте завдання: показати статті лише з певної категорії, але категорія — пов'язаний запис. В Directus це робиться через filter[category][slug][_eq]=tech. Однак OR-умови з вкладеними зв'язками можуть призвести до некоректних результатів. Наприклад, filter[_or][0][title][_icontains]=react&filter[_or][1][content][_icontains]=react працює, але якщо потрібно об'єднати з фільтром по автору — синтаксис ускладнюється. Ми використовуємо кастомні ендпоінти на Flows для складної логіки.
Агрегації з групуванням
Для дашбордів часто потрібна сума продажів по місяцях. Directus підтримує aggregate[sum]=total&groupBy[]=status, але в одному запиті не можна згрупувати за двома різними полями. Ми застосовуємо SDK і пишемо кастомні SQL-запити через міграції.
Як ми це робимо: стек та кейс з практики
Проект нашого клієнта — інтернет-магазин на Next.js + Directus 10. Завдання: побудувати API для каталогу з фільтрами (ціна, бренд, характеристики), пошуком через Meilisearch, realtime-оновленням кошика. Стек: Directus (REST + WebSocket), Meilisearch для повнотекстового пошуку, Redis для кешування.
Кейс з агрегацією: Потрібно було вивести середній чек по днях. Через стандартний REST це неможливо — лише один тип агрегації на запит. Написали кастомний ендпоінт на Flows з SQL-запитом:
Документація Directus: Flows
SELECT DATE(date_created) as day, AVG(total) as avg_check
FROM orders
WHERE status = 'paid'
GROUP BY day
ORDER BY day;
Кастомний SQL-запит через Flows виконується в 5 разів швидше за аналогічний GraphQL-запит. Результат: 1 запит замість 30, швидкість дашборду зросла в 4 рази. Замовник заощадив $2000 на місяць на кешуванні та CDN. Середній час відповіді API зменшився з 300 мс до 50 мс після оптимізації.
Процес роботи
- Аналітика: ревізія поточних запитів, виявлення вузьких місць (Core Web Vitals, кількість запитів).
- Проектування: обираємо REST, GraphQL або WebSocket під завдання. Прописуємо схему реляцій і фільтрів.
- Реалізація: налаштування ендпоінтів, кастомних Flows, оптимізація через
fields і deep. Для пошуку підключаємо Meilisearch або Elasticsearch.
- Тест: навантажувальне тестування (k6), перевірка на 1000 concurrent запитів.
- Деплой: налаштування rate limiting, CORS, SSL, кешування (Redis/Varnish).
Налаштування GraphQL в Directus
Увімкніть GraphQL, вказавши в .env:
GRAPHQL_SDLFILE=/tmp/schema.graphql
GraphQL доступний по /graphql. В production вимикаємо інтроспекцію через GRAPHQL_INTROSPECTION=false. Використовуйте мутації для створення записів і subscription для realtime.
Причини швидшої роботи REST API
В Directus REST API використовує кешування на рівні бази даних (ключі запиту), а GraphQL — ні. Для простих вибірок REST дає менший latency (на 20–30%). Але GraphQL зручніший для складних вкладених запитів з різними полями. Вибір залежить від задачі: для публічних ендпоінтів — REST, для адмін-панелей — GraphQL.
Інтеграція пошуку через Meilisearch
Meilisearch підключається як сервіс в Directus через Hook або Flow. Налаштовуємо індексацію полів, релевантність та фільтри. Приклад конфігурації:
{
"index": "articles",
"primaryKey": "id",
"searchableAttributes": ["title", "content"],
"filterableAttributes": ["status", "category_id"]
}
Після синхронізації дані доступні через окремий ендпоінт. Результат — пошук за 10-50 мс замість 500+ мс при повнотекстовому пошуку в PostgreSQL.
Порівняння REST та GraphQL
| Критерій |
REST |
GraphQL |
| Кешування |
Вбудоване (URL як ключ) |
Відсутнє (потрібно налаштовувати) |
| Overfetching |
Так (якщо не вказано fields) |
Ні (повертає лише запитані поля) |
| Вкладені запити |
Через deep, складно для OR |
Природно (query language) |
| Продуктивність |
Вища для простих вибірок |
Нижча через парсинг запитів |
| Підходить для |
Публічні API, кешування |
Складні клієнтські інтерфейси |
Типові помилки при налаштуванні Directus API
Типові помилки
| Помилка |
Наслідок |
Рішення |
| Неправильний синтаксис deep |
Помилка 500, порожня відповідь |
URL-encode параметри: deep[comments][_sort]=-date |
| Відсутність індексів |
Full scan, повільні запити |
Створити індекси на часто фільтровані поля |
| Занадто широкі fields |
Високий трафік, повільна відповідь |
Вказувати лише необхідні поля |
| Ігнорування кешу |
Надмірне навантаження на БД |
Увімкнути кешування через Varnish/Cloudflare |
Що входить в роботу
- Аудит поточних запитів та оптимізація.
- Налаштування REST, GraphQL або WebSocket під завдання.
- Написання кастомних ендпоінтів на Flows для складної логіки.
- Інтеграція пошуку (Meilisearch/Elasticsearch).
- Налаштування rate limiting, кешування, безпеки.
- Документація по API (OpenAPI/Swagger).
- Навчання команди замовника.
- Підтримка 1 місяць після запуску.
Строки
Базове налаштування REST/GraphQL — від 2 до 5 днів. Кастомні ендпоінти та складні агрегації — від 5 до 10 днів. Точні строки оцінюємо після аудиту.
Ми даємо гарантію на оптимізацію — якщо швидкість не зросте, повертаємо гроші. Отримайте безкоштовну консультацію по вашому проекту. Замовте аудит Directus API — ми запропонуємо оптимізоване рішення.
Розробка API: REST, GraphQL, WebSocket, tRPC
До нас приходить клієнт з Postman-колекцією на 200 ендпоінтів і каже: «Все працює, але фронтенд гальмує». Відкриваємо Network-вкладку — 47 послідовних запитів на завантаження однієї сторінки дашборду. Кожен чекає попереднього. Це не проблема швидкості сервера — це проблема архітектури API. За 10 років на ринку ми перепроектували не один десяток таких інтеграцій, і гарантуємо: правильний протокол і контракт вирішують проблему докорінно.
Коли REST перестає справлятися
REST добре працює для простих CRUD-операцій. Але як тільки поруч з веб-інтерфейсом з'являється мобільний додаток, починається over-fetching: мобілка запитує /api/users/123 і отримує об'єкт на 4KB, хоча їй потрібні тільки name і avatar. Помножте на список з 50 користувачів — 200KB трафіку замість 8KB.
GraphQL вирішує це через selection sets. Клієнт описує саме ті поля, які йому потрібні, і сервер повертає саме їх. На проекті з React Native + Next.js ми переїхали з REST на Apollo Server: розмір payload на головному екрані впав з 340KB до 28KB — економія трафіку склала 92%. Сертифіковані інженери команди підтверджують: типові болі при впровадженні GraphQL — N+1 query. Резолвер для поля author у поста викликає SELECT * FROM users WHERE id = ? для кожного поста у списку. На сторінці з 20 постами — 21 запит до бази. Вирішується через DataLoader — він батчить запити і перетворює їх в один SELECT * FROM users WHERE id IN (...).
Що таке tRPC і чим він кращий за REST/GraphQL?
Якщо весь стек на TypeScript (Next.js + Node/Bun), tRPC прибирає цілий шар проблем. Ви визначаєте процедуру на сервері — клієнт отримує повний тайп-сейфти автоматично, без генерації коду і без Swagger. Перейменували поле в схемі Zod — TypeScript підсвітить всі місця на фронтенді, де воно використовується. tRPC зменшує кількість коду в 2 рази порівняно з REST + Swagger + openapi-typescript: не потрібно підтримувати окрему специфікацію і генерувати типи — все виводиться з рантаймових валідаторів. Однак tRPC не підходить, якщо API споживають сторонні клієнти або мобільні додатки на інших мовах — у таких випадках використовуємо GraphQL або REST з OpenAPI-специфікацією.
WebSocket і реальний час: коли SSE, коли WS?
HTTP-поллінг кожні 5 секунд — це ілюзія реального часу з затримкою до 5 секунд і безкорисним навантаженням на сервер. Для чатів, live-нотифікацій, спільного редагування — WebSocket або Server-Sent Events. SSE — односпрямований потік від сервера до клієнта, працює поверх звичайного HTTP, автоматично перепідключається. Підходить для нотифікацій, стрімінгу даних, прогрес-барів. WebSocket — двоспрямований, потрібен для чатів і колаборативних функцій. Досвід показує: 80% завдань «реального часу» вирішуються через SSE, а не WebSocket — менше інфраструктурних складнощів.
Типова помилка: відкривати WebSocket-з'єднання на кожен компонент сторінки. На одному проекті дашборд відкривав 12 паралельних WS-з'єднань. Правильно — один connection manager на рівні додатку, підписки через нього. В результатах роботи ми завжди передаємо схему з'єднання і готове рішення.
| Протокол |
Типізація |
Over-fetching |
Версіонування |
Real-time |
| REST |
Слабка (OpenAPI) |
Присутній |
URL / Header |
Поллінг |
| GraphQL |
Сильна (SDL) |
Немає |
Deprecation |
Subscriptions |
| tRPC |
Повна (TypeScript) |
Немає |
TypeScript checks |
Subscriptions (optional) |
Swagger / OpenAPI як контракт
Документація, написана постфактум — застаріває на наступний день після релізу. Ми пишемо специфікацію OpenAPI 3.1 до початку розробки, вона стає контрактом між фронтендом і бекендом. Фронтенд генерує типи через openapi-typescript, бекенд валідує вхідні дані через згенеровані схеми. Розбіжність контракту з реалізацією ловиться на CI, а не на рев'ю. Для Laravel — l5-swagger або dedoc/scramble. Для Node.js — @fastify/swagger або Zod + zod-to-openapi.
Як правильно аутентифікувати API?
JWT з довго живучними access-токенами без ротації — джерело проблем при компрометації. Правильна схема: access-токен на 15 хвилин, refresh-токен на 30 днів з ротацією при кожному використанні. Refresh-токен зберігається в httpOnly cookie, access-токен — в пам'яті (не в localStorage). Для міжсервісної взаємодії — API Keys з scope-обмеженнями або mTLS. OAuth 2.0 з PKCE для публічних клієнтів (SPA, мобілки).
Версіонування і зворотна сумісність
Ламаючі зміни в API без версіонування ламають клієнтів. Три підходи ми використовуємо в проектах:
| Метод |
Приклад |
Коли застосовувати |
| URL-версіонування |
/api/v2/ |
REST API з довгою підтримкою legacy |
| Header-версіонування |
Accept: application/vnd.api+json;version=2 |
Мінімальні зміни в URL |
| Еволюційне (deprecation) |
Додавання полів, deprecated-директива GraphQL |
Для GraphQL — плавний вивід полів |
Зворотну сумісність ми гарантуємо через автомат-перевірки (oasdiff) на CI.
Як ми розробляємо API: покроковий план
-
Аналітика — аудит поточних інтеграцій, складання схеми даних, вибір протоколу (REST/GraphQL/tRPC/WebSocket).
-
Проектування контракту — OpenAPI або SDL (GraphQL) до першого рядка коду.
-
Розробка — реалізація за контрактом, модульні тести на кожен ендпоінт.
-
Навантажувальне тестування — k6: 500 віртуальних користувачів, 10 хвилин, p95 latency ≤ 200ms.
-
Деплой — CI/CD з перевіркою зворотної сумісності, автоматична публікація документації.
-
Навчання команди — передача Postman-колекції або Playground, інструкція з підключення.
Типові помилки, які ми виключаємо
- N+1 при запитах без DataLoader.
- Відсутність rate limiting — DDOS через неавторизовані ендпоінти.
- Зберігання access-токена в localStorage.
- Відкриття множини WebSocket-з'єднань замість одного connection manager.
- Документація, не оновлена після релізу.
Що входить в роботу (deliverables)
- OpenAPI 3.1 специфікація (або SDL для GraphQL).
- Згенеровані клієнтські типи для TypeScript / Dart / Kotlin.
- Набір автотестів з покриттям всіх ендпоінтів (модульні + інтеграційні).
- Навантажувальні тести (k6) і звіт (p50/p95/p99 latency, RPS).
- Документація в Swagger UI / Redoc / GraphiQL.
- Навчання команди (2–4 години воркшопу).
- Підтримка протягом 30 днів після здачі (за договором).
Наш досвід
-
10+ років на ринку розробки API.
-
200+ завершених проектів (REST, GraphQL, WebSocket, tRPC).
-
50+ сертифікованих інженерів (AWS, Kubernetes, API Design).
- Економія на трафіку в середньому 85% при переході з REST на GraphQL для мобільних додатків.
-
100% зворотна сумісність — жодного зламаного клієнта за останні 3 роки.
Терміни
Розробка API для типового SaaS-проекту з 30–50 ендпоінтами: від 3 до 8 тижнів залежно від складності бізнес-логіки та кількості зовнішніх інтеграцій. Міграція існуючого REST API на GraphQL — від 2 до 6 тижнів. Додавання WebSocket-шару до готового бекенду — від 1 до 3 тижнів. Вартість розраховується індивідуально після аудиту. Отримайте консультацію — зв'яжіться з нами, щоб обговорити ваш проект.