Інтеграція KeystoneJS з фронтендом через GraphQL API
Проблема: KeystoneJS генерує GraphQL API, але при інтеграції з фронтендом часто виникає розузгодження типів — фронтенд використовує поля, яких немає в схемі, або допускає помилки в мутаціях. Пагінація через take/skip без правильного кешування призводить до дублювання записів, а аутентифікація потребує акуратного налаштування middleware. Ми, команда з досвідом роботи з KeystoneJS понад 5 років, налаштовували цю зв'язку для 30+ проєктів, що дозволило скоротити кількість багів на 90% і прискорити релізи вдвічі. Типова інтеграція на Next.js + Apollo Client з codegen займає 3–5 днів під ключ. Отримайте безкоштовний аудит вашої схеми KeystoneJS — ми знайдемо вузькі місця та запропонуємо план інтеграції.
Що генерує KeystoneJS
Для кожного List, наприклад Post, створюються стандартні запити та мутації (KeystoneJS GraphQL API):
-
post(where: PostWhereUniqueInput!): Post
-
posts(where: PostWhereInput, orderBy: [...], take: Int, skip: Int): [Post!]
-
postsCount(where: PostWhereInput): Int
-
createPost(data: PostCreateInput!): Post
-
createPosts(data: [PostCreateInput!]!): [Post]
-
updatePost(where: PostWhereUniqueInput!, data: PostUpdateInput!): Post
-
deletePost(where: PostWhereUniqueInput!): Post
Це позбавляє від написання повторюваного коду. Однак без codegen легко помилитися в імені поля або забути вибрати вкладені зв'язки (N+1 проблема), що збільшує вартість доопрацювань на 30-50%.
Як налаштувати Apollo Client для KeystoneJS?
Apollo Client — найпопулярніший GraphQL-клієнт для React. Налаштування включає HTTP-посилання на ендпоінт, middleware для аутентифікації та обробку помилок. Ось приклад для Next.js з підтримкою cookie-сесій (Apollo Client documentation):
// lib/apollo.ts
import { ApolloClient, InMemoryCache, createHttpLink, from } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
import { onError } from '@apollo/client/link/error';
const httpLink = createHttpLink({
uri: process.env.NEXT_PUBLIC_KEYSTONE_URL + '/api/graphql',
credentials: 'include',
});
const authLink = setContext((_, { headers }) => ({
headers: {
...headers,
authorization: getToken() ? `Bearer ${getToken()}` : '',
},
}));
const errorLink = onError(({ graphQLErrors }) => {
if (graphQLErrors?.some(e => e.extensions?.code === 'UNAUTHENTICATED')) {
window.location.href = '/login';
}
});
export const apolloClient = new ApolloClient({
link: from([errorLink, authLink, httpLink]),
cache: new InMemoryCache({
typePolicies: {
Query: {
fields: {
posts: {
keyArgs: ['where', 'orderBy'],
merge: (existing = [], incoming) => [...existing, ...incoming],
},
},
},
},
}),
});
Цей блок об'єднує аутентифікацію та пагінацію. Порівняно з прямим використанням fetch, Apollo Client дає автоматичне кешування, інвалідацію та зручні хуки — скорочення коду на 40%.
Чому codegen прискорює розробку в 3 рази
GraphQL Code Generator створює TypeScript-типи та хуки за вашою схемою. Конфігурація тривіальна:
# codegen.yml
schema: http://localhost:3000/api/graphql
documents: "src/**/*.graphql"
generates:
src/generated/graphql.ts:
plugins:
- typescript
- typescript-operations
- typescript-react-apollo
Приклад запиту:
# src/queries/posts.graphql
query GetPosts($where: PostWhereInput, $take: Int, $skip: Int) {
posts(where: $where, take: $take, skip: $skip, orderBy: [{ publishedAt: desc }]) {
id, title, slug, publishedAt, status
author { id, name }
tags { id, name }
}
postsCount(where: $where)
}
Після генерації ви отримуєте готові хуки з автодоповненням. Це повністю виключає помилки в назвах полів і запитах, заощаджуючи до 60% часу на налагодження.
Як використовувати в Next.js Server Components
У Server Components запити виконуються на стороні сервера. Використовуємо серверний клієнт Apollo:
// app/blog/page.tsx
import { getClient } from '@/lib/apollo-server';
import { GetPostsDocument } from '@/generated/graphql';
export default async function BlogPage({ searchParams }) {
const page = Number(searchParams.page) || 1;
const { data } = await getClient().query({
query: GetPostsDocument,
variables: { where: { status: { equals: 'published' } }, take: 10, skip: (page - 1) * 10 },
});
return <PostGrid posts={data.posts} total={data.postsCount} page={page} />;
}
Мутації в Client Components
Для операцій запису використовуємо клієнтські компоненти:
'use client';
import { useMutation } from '@apollo/client';
import { CreatePostDocument } from '@/generated/graphql';
export function NewPostForm() {
const [createPost, { loading, error }] = useMutation(CreatePostDocument, {
update(cache, { data }) {
cache.evict({ fieldName: 'posts' });
},
});
const handleSubmit = async (formData) => {
const { data } = await createPost({
variables: {
data: { title: formData.title, slug: formData.slug, content: { document: formData.content }, author: { connect: { id: currentUserId } }, status: 'draft' },
},
});
router.push(`/admin/posts/${data?.createPost?.id}`);
};
}
Порівняння Apollo Client vs fetch
| Критерій |
Apollo Client |
fetch + ручне кешування |
| Кешування |
автоматичне (InMemoryCache) |
ручне через React Query/SWR |
| Типізація |
інтеграція з codegen |
окремі типи |
| Аутентифікація |
middleware |
додатковий код |
| Пагінація |
keyArgs, merge |
ручна логіка |
| Розмір бандла |
~30 kB |
~0 kB (але + React Query ~11 kB) |
Використання Apollo Client скорочує час розробки на 2–3 дні порівняно з чистим fetch, що суттєво економить бюджет проєкту.
Як уникнути типових помилок при інтеграції?
| Помилка |
Наслідок |
Рішення |
| Відсутність codegen |
Помилки в назвах полів, довга налагодження |
Налаштувати codegen на старті проєкту |
| Неправильна конфігурація кешу |
Дублювання записів при пагінації |
Налаштувати merge-політику через keyArgs |
| Ігнорування аутентифікації |
Неавторизовані запити, витік даних |
Додати middleware з перевіркою токена |
| Використання Client Components для всіх запитів |
Збільшення розміру бандла та часу завантаження |
Винести запити читання в Server Components |
| Невірний ключ кешу для пагінації |
Перезапис даних при різних сортуваннях |
Використовувати keyArgs з where і orderBy |
Покроковий план інтеграції
- Аналіз схеми KeystoneJS — перевірка Lists, зв'язків, дозволів та тригерів.
- Налаштування Apollo Client — створення серверного та клієнтського інстансів з middleware.
- Конфігурація codegen — генерація TypeScript-типів та хуків.
- Реалізація запитів — вибраний List, пагінація, сортування.
- Реалізація мутацій — CRUD для адміністративних інтерфейсів.
- Аутентифікація — сесійна або JWT, захист маршрутів.
- Тестування — Unit-тести на Apollo MockedProvider.
Що входить в послугу інтеграції
- Аудит існуючої конфігурації KeystoneJS.
- Налаштування Apollo Client з оптимізацією кешу.
- Генерація типів GraphQL Code Generator.
- Реалізація запитів і мутацій для критичних List’ів.
- Інтеграція аутентифікації (сесійна / JWT).
- Документація з використання згенерованих хуків.
- Підтримка 2 тижні після здачі.
Гарантуємо, що всі запити проходять code review і тестування. Замовте консультацію, щоб ми проаналізували вашу поточну схему та запропонували оптимальний план інтеграції.
Досвід нашої команди
Ми працюємо з KeystoneJS та GraphQL з 2019. За цей час виконали 30+ проєктів, включаючи інтеграцію з інтернет-магазинами, SaaS-платформами та корпоративними порталами. Середній NPS — 9.2. Економія бюджету замовників склала до 60% у порівнянні з альтернативними рішеннями. Зв'яжіться з нами, щоб обговорити ваш проєкт.
Headless CMS: Strapi, Directus, Sanity, Contentful, Drupal
Традиційна CMS хороша до моменту, коли дизайнер каже «хочу анімацію при скролі з parallax», фронтенд — «нам потрібен React», а SEO-спеціаліст — «чому TTFB 3.4 секунди». У цей момент монолітна архітектура починає заважати всім одразу. Я стикався з цим десятки разів: сайт на WordPress з ACF розростається до 47 плагінів, адмінка гальмує, а кожен редизайн перетворюється на переписування шаблонів.
Headless CMS відокремлює управління контентом від його представлення. Редактори працюють у зручному інтерфейсі, розробники отримують дані через API і будують фронтенд на будь-якому стеку. Звучить просто. На практиці — вибір CMS, моделювання даних і налаштування API займають значну частину проєкту. За понад 5 років ми провели понад 50 впроваджень — розповім, як не наступити на типові граблі.
Чому headless CMS вигідніша за моноліт?
Монолітна CMS (WordPress, Joomla, Drupal у класичному режимі) змішує бекенд і фронтенд. Будь-яка зміна верстки — це зміна шаблонів, часто з ризиком зламати адмінку. Headless дає свободу: фронтенд на React, Vue або Svelte, а контент живе окремо. Результат — швидкість завантаження (LCP часто падає з 4–6 с до 1–1,5 с), безпека (нема публічного доступу до адмін-панелі), масштабування (контент віддається через CDN без навантаження на сервер). Плюс можливість перевикористовувати контент у мобільних додатках, кіосках, email-розсилках через єдиний API. На одному проєкті це заощадило 80 годин переробок і $4000 бюджету.
Яку headless CMS обрати під проєкт?
Нема універсального інструменту. Вибір залежить від команди, складності контенту та інфраструктури. Розберемо ключові варіанти.
Strapi — open-source, self-hosted, Node.js. Підходить командам, яким потрібен контроль над даними та можливість кастомізації API. Плагінна архітектура дозволяє додавати кастомні маршрути, middleware, lifecycle hooks. REST і GraphQL з коробки. Розгортається за годину — в 3 рази швидше за Drupal. Слабке місце — версії v4 та v5 несумісні між собою, міграція болюча. Наш досвід показує: для стартапів та середніх проєктів Strapi — оптимальний баланс гнучкості та швидкості.
Directus — теж open-source, але інший підхід: не генерує схему, а обгортає існуючу базу даних (PostgreSQL, MySQL, SQLite) у REST/GraphQL API. Якщо база даних вже є — Directus підключається до неї без міграцій. Зручно для проєктів, де дані вже живуть у PostgreSQL і потрібен швидкий admin UI + API. Економія часу на етапі інтеграції — до 30%.
Sanity — хмарна CMS з real-time редактором. Відмінна риса — GROQ (Graph-Relational Object Queries), власна мова запитів, яка потужніша за REST для складних зв'язків між документами. Portable Text для структурованого контенту. Підходить для медіа, видавництв, маркетингових сайтів з нестандартними редакційними процесами. Гарантує швидкість навіть при 500+ одночасних редакторах — перевірено на проєктах з щохвилинним оновленням стрічки новин.
Contentful — enterprise хмарна CMS. Сильна сторона — локалізація (до 1000 локалей), багатий SDK для всіх платформ, Contentful Apps для кастомних UI. Слабка — ціна при масштабуванні та обмежена гнучкість моделей даних порівняно з open-source альтернативами.
Drupal — не headless у чистому вигляді, але з модулем JSON:API та GraphQL перетворюється на потужний API-first бекенд. Сильна сторона — зрілість, гранулярні права доступу, enterprise-клієнти (NASA, weather.com). Поріг входу високий, для складних державних або корпоративних порталів альтернатив мало. Ми використовуємо його тільки коли потрібна строга ієрархія ролей та аудит доступу.
| CMS |
Хостинг |
API |
Найкращий сценарій |
| Strapi |
Self-hosted / Cloud |
REST, GraphQL |
Стартапи, кастомізація |
| Directus |
Self-hosted / Cloud |
REST, GraphQL |
Обгортка над existing DB |
| Sanity |
Хмара |
GROQ, GraphQL |
Медіа, складний контент |
| Contentful |
Хмара |
REST, GraphQL |
Enterprise, локалізація |
| Drupal |
Self-hosted |
JSON:API, GraphQL |
Держсектор, складні права |
Які наслідки неправильного моделювання контенту?
Моделювання контенту — критичний етап. Помилка на цьому етапі коштує дорого. Типова проблема: поле body типу rich text для всього. Через пів року контент-менеджер хоче вставити відео між абзацами, додати pull quote з кастомним стилем, вбудувати інтерактивну таблицю. Rich text це не дозволяє. Рішення — Portable Text (Sanity) або кастомні компоненти в Strapi/Directus через Dynamic Zone. Ми завжди закладаємо на етапі проєктування 2–3 ітерації з замовником, щоб схема покривала 90% майбутніх кейсів. На одному проєкті це заощадило 80 годин переробок — бюджет на моделювання окупився втричі, а економія склала понад $4000.
Як ми будуємо проєкти на headless CMS
Фронтенд під headless CMS практично завжди йде на Next.js (App Router) або Nuxt. Для Contentful та Sanity — ISR: сторінки статично генеруються при білді, оновлюються через revalidatePath() при зміні контенту через webhook. Для Strapi/Directus з частим оновленням даних — SSR з cache: 'no-store' або SWR на клієнті.
Кейс: редизайн корпоративного сайту виробничої компанії. Попередній сайт — WordPress з ACF, 200+ сторінок, 4 мови. Проблеми: TTFB 3,8 с, редактори скаржилися на повільну адмінку.
Перейшли на Strapi (self-hosted, PostgreSQL), Next.js App Router. Контентна модель: Page з Dynamic Zone (секції Hero, TextBlock, Gallery, TeamGrid, ContactForm). Локалізація через Strapi i18n plugin + next-intl на фронтенді. Деплой фронтенду на Vercel з ISR, ревалідація через Strapi webhook на entry.publish.
TTFB з 3,8 с впав до 180 мс (статика з CDN) — різниця в 21 раз. Редактори отримали чистий інтерфейс без 47 плагінів. Вартість хостингу знизилася на $200 на місяць — це економія $2400 на рік.
Для розуміння headless CMS та TTFB рекомендую базові статті, зокрема офіційну документацію Strapi та Wikipedia.
Процес впровадження розбитий на етапи:
- Аудит контентних потреб — збираємо всі типи контенту, зв'язки, вимоги до локалізації, інтеграції.
- Проєктування схеми даних — створюємо моделі, поля, валідацію, ролі доступу. Документуємо в Swagger/OpenAPI.
- Налаштування CMS та API — розгортаємо обрану CMS, налаштовуємо REST/GraphQL endpoints, плагіни, webhooks.
- Розробка фронтенду — підключаємо Next.js/Nuxt, налаштовуємо ISR/SSR, компоненти секцій, роутинг.
- Міграція контенту (якщо є legacy) — автоматичне завантаження через API або скрипти.
- Тестування — перевірка API endpoints, регресія, навантажувальне тестування, Core Web Vitals.
- Деплой — налаштування CDN, SSL, CI/CD, моніторинг.
Скільки часу займає впровадження?
Стандартний шлях включає всі етапи. Міграція з WordPress на headless CMS займає стільки ж часу, скільки сам проєкт — часто більше. Особливо якщо в WordPress накопичені кастомні поля через ACF з нестандартною структурою. Наші середні терміни:
| Тип проєкту |
Термін |
| Простий сайт на Strapi + Next.js |
4–8 тижнів |
| Багатомовний корпоративний сайт |
8–16 тижнів |
| Міграція з WordPress на headless |
+4–8 тижнів до основного |
| Drupal enterprise-портал |
3–6 місяців |
Вартість розраховується індивідуально після брифу. Економія на хостингу за рахунок статичної генерації — до 40% на місяць.
Неочевидні моменти при виборі headless CMS
- Перевірте, чи підтримує CMS мультисайтинг — якщо плануєте кілька доменів, багато open-source рішень не вміють розділяти контент за доменами без костилів.
- Уточніть формат історії змін — Strapi зберігає drafts тільки для publish-версій, а Directus — повний аудит всіх змін.
- Протестуйте швидкість роботи admin panel на слабкому інтернеті — Sanity працює в реальному часі через WebSocket, що може бути проблемою при поганому з'єднанні.
- Оцініть складність кастомних полів — у Contentful додавання нового поля вимагає деплою, у Strapi — тільки перезапуску сервера.
- Дізнайтеся про ліцензійні обмеження — Strapi v5 перейшов на Elastic License, що може вплинути на комерційне використання.
Що входить в роботу
- Документація схеми даних та API (Swagger/OpenAPI)
- Налаштована адмін-панель з правами доступу
- Навчання редакторів (2-годинна сесія)
- Тестовий стенд на час розробки
- Гарантія 1 місяць на баги після запуску
- Підтримка після релізу (включаючи хотфікси 24/7)
Headless CMS розробка — це не просто заміна інструменту, а зміна парадигми роботи з контентом. Ми допомагаємо зробити цей перехід без простоїв та втрати даних. Отримайте консультацію та попередню оцінку — залиште заявку на сайті. Замовте впровадження headless CMS з гарантією результату.