Інтеграція Cockpit CMS з фронтендом через API
Зібрали статичний сайт на Next.js, контент зберігається в Cockpit CMS. Все працювало в dev-режимі, але на проді — помилка 401. Виявилося, API-токен не передавався в заголовках статичної збірки. Типова історія: headless CMS дає гнучкість, але вимагає правильної архітектури запитів. Ми розберемо, як налаштувати інтеграцію, щоб уникнути таких сюрпризів. Нижче — повний цикл: від налаштування CORS до деплою з ISR. Досвід показує, що типову інтеграцію можна виконати за 2–4 дні, заощадивши до 50% бюджету порівняно з Strapi або Contentful.
Чому Cockpit CMS зручний для фронтенду?
Cockpit — легка headless CMS без жорсткої схеми. Ви визначаєте колекції та синглтони через адмінку, а фронтенд отримує готові JSON-об'єкти. Це дозволяє змінювати структуру контенту без міграцій бази. Для статичних сайтів (SSG) і динамічних сторінок (ISR) Cockpit дає єдину точку входу. Згідно з офіційною документацією Cockpit, це одна з найпростіших headless CMS у розгортанні — 10 хвилин до першого запиту.
Які проблеми вирішуємо при інтеграції?
-
Авторизація: токен не повинен потрапляти в клієнтський код. Ми передаємо його через змінні оточення сервера (
process.env.COCKPIT_API_TOKEN).
- CORS: якщо Cockpit розгорнуто на іншому домені, фронтенд не зможе напряму робити запити. Налаштовуємо CORS-заголовки на сервері Cockpit (
cockpit/config/cors.php).
- Кешування: часті запити до API сповільнюють завантаження. Використовуємо ISR в Next.js або Redis-кеш для типових запитів.
- Зображення: Cockpit генерує URL з параметрами трансформації на льоту. Не зберігайте посилання на оригінали — завжди використовуйте
/api/cockpit/image.
Як ми це робимо: повний кейс
Для одного проєкту з 3 колекціями (статті, послуги, відгуки) та синглтоном налаштувань ми реалізували інтеграцію за 3 дні. Стек: Next.js 14, Cockpit 2.3, TypeScript на сервері, ISR для кожного типу контенту.
Базовий клієнт
// lib/cockpit.ts
class CockpitClient {
private baseUrl: string;
private token: string;
constructor(url: string, token: string) {
this.baseUrl = url.replace(/\/$/, '');
this.token = token;
}
private async request(path: string, options: RequestInit = {}) {
const res = await fetch(`${this.baseUrl}${path}`, {
...options,
headers: {
'Content-Type': 'application/json',
'Cockpit-Token': this.token,
...options.headers,
},
next: { revalidate: 3600 }, // Next.js ISR
});
if (!res.ok) throw new Error(`Cockpit API error: ${res.status}`);
return res.json();
}
// Записи колекції
async getCollection(name: string, params: CollectionParams = {}) {
const body = {
limit: params.limit || 100,
skip: params.skip || 0,
sort: params.sort || { _created: -1 },
filter: params.filter || {},
populate: params.populate || 1,
fields: params.fields,
};
return this.request(`/api/collections/get/${name}`, {
method: 'POST',
body: JSON.stringify(body),
});
}
// Один запис за ID
async getCollectionItem(collection: string, id: string) {
return this.request(`/api/collections/get/${collection}`, {
method: 'POST',
body: JSON.stringify({ filter: { _id: id }, limit: 1 }),
});
}
// Singleton
async getSingleton(name: string) {
return this.request(`/api/singletons/get/${name}`);
}
// Зображення з трансформацією
getImageUrl(path: string, options: ImageOptions = {}) {
const params = new URLSearchParams({
src: path,
w: String(options.width || 800),
h: String(options.height || 600),
m: options.mode || 'thumbnail',
q: String(options.quality || 80),
o: '1',
});
return `${this.baseUrl}/api/cockpit/image?${params}&token=${this.token}`;
}
}
export const cockpit = new CockpitClient(
process.env.COCKPIT_URL!,
process.env.COCKPIT_API_TOKEN!
);
Next.js: статичні сторінки
// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
const { entries } = await cockpit.getCollection('posts', {
filter: { published: true },
fields: { slug: 1 },
});
return entries.map((post: any) => ({ slug: post.slug }));
}
export default async function PostPage({ params }) {
const { entries } = await cockpit.getCollection('posts', {
filter: { slug: params.slug, published: true },
limit: 1,
populate: 2,
});
if (!entries.length) notFound();
const post = entries[0];
return (
<article>
<h1>{post.title}</h1>
{post.image && (
<img
src={cockpit.getImageUrl(post.image.path, { width: 1200, height: 630 })}
alt={post.title}
/>
)}
<div dangerouslySetInnerHTML={{ __html: post.description }} />
</article>
);
}
Реалізація пошуку
Cockpit REST API не підтримує повнотекстовий пошук нативно. Реалізуємо через regex-фільтр:
async function searchPosts(query: string) {
const { entries } = await cockpit.getCollection('posts', {
filter: {
published: true,
$or: [
{ title: { $regex: query, $options: 'i' } },
{ description: { $regex: query, $options: 'i' } },
],
},
limit: 20,
});
return entries;
}
Для повноцінного пошуку — індексуємо в Algolia через webhook при змінах.
GraphQL API
Cockpit також надає GraphQL endpoint на /api/graphql:
query {
posts: collectionGet(collection: "posts", limit: 10, sort: {_created: -1}) {
entries {
_id
title
slug
image
}
total
}
homepage: singletonGet(singleton: "homepage") {
hero_title
hero_subtitle
hero_image
}
}
Покрокова інструкція з налаштування
- Встановіть Cockpit на сервер (документація: https://cockpitcms.io).
- Створіть колекцію в адмін-панелі, додайте поля.
- Згенеруйте API-токен у налаштуваннях.
- Налаштуйте CORS у
cockpit/config/cors.php.
- Реалізуйте клієнт, як показано вище.
- Використовуйте ISR у Next.js для кешування.
Порівняння Cockpit з іншими headless CMS
| Критерій |
Cockpit |
Strapi |
Contentful |
| Час розгортання |
10 хвилин |
15 хвилин |
хмарна |
| Безкоштовно |
Так |
Так |
обмежено |
| REST+GraphQL |
Так |
Так |
Так |
| Локалізація |
нативно |
плагін |
вбудована |
Cockpit виграє у простоті: розгортання в 1.5 рази швидше за Strapi, а для невеликих проєктів він економить до 40% витрат на інфраструктуру.
Процес роботи
| Етап |
Тривалість |
Результат |
| Аналіз схеми контенту |
0.5–1 день |
Список колекцій, синглтонів, екшенів |
| Налаштування API та CORS |
0.5 дня |
Робочий клієнт auth, фільтри |
| Реалізація інтеграції |
1–2 дні |
Код клієнта, статичні сторінки, ISR |
| Тестування |
0.5 дня |
Перевірка всіх точок входу, кешування |
| Деплой та документування |
0.5 дня |
Readme, доступи, інструкція |
Терміни та що входить
Інтеграція 2–3 колекцій + синглтон + зображення через CDN займає від 2 до 4 днів. В результаті ви отримуєте:
- Типізований клієнт на TypeScript
- Готові сторінки зі статичною генерацією та ISR
- Налаштований CORS та безпечну передачу токена
- Документацію з оновлення контенту
- Консультацію 1 година з експлуатації
Типові помилки
- Токен у клієнті: ніколи не передавайте токен через
getServerSideProps або клієнтські fetch — використовуйте серверні компоненти Next.js.
- Відсутність populate: якщо в колекції є посилання на інші записи, не забудьте
populate: 1, інакше отримаєте лише ID.
- Скидання кешу: при зміні контенту в Cockpit потрібно скинути ISR-кеш. Рішення — webhook на
revalidatePath() у Next.js.
Отримайте консультацію з інтеграції Cockpit CMS — оцінимо проєкт за 1 день. Свяжіться з нами, ми маємо понад 40 успішних інтеграцій та гарантуємо стабільну роботу.
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 з гарантією результату.