Інтеграція Ghost Content API з кастомним фронтендом
Ghost Content API — публічний REST API тільки для читання. Повертає посты, сторінки, теги, авторів, налаштування сайту. Використовується для headless-сценаріїв: Next.js, Astro, Gatsby замість нативних Handlebars-тем.
Аутентифікація та ключі
Content API Key створюється в Ghost Admin → Settings → Integrations → Add custom integration. Ключ передається як query-параметр або заголовок — він публічний (read-only), безпечний для використання на фронтенді.
// lib/ghost.ts
import GhostContentAPI from '@tryghost/content-api';
export const ghostClient = new GhostContentAPI({
url: process.env.GHOST_URL!, // https://myblog.com
key: process.env.GHOST_CONTENT_API_KEY!,
version: 'v5.0',
});
Основні методи SDK
// Список постів з пагінацією
const posts = await ghostClient.posts.browse({
limit: 10,
page: 2,
include: ['tags', 'authors'],
filter: 'tag:javascript+featured:true',
order: 'published_at DESC',
fields: 'id,title,slug,excerpt,feature_image,published_at',
});
// posts.meta.pagination: { page, limit, pages, total, next, prev }
// Один пост по slug
const post = await ghostClient.posts.read(
{ slug: 'my-post-slug' },
{ include: ['tags', 'authors'], formats: ['html', 'plaintext'] }
);
// Pages (Сторінки)
const pages = await ghostClient.pages.browse({ limit: 'all' });
// Теги
const tags = await ghostClient.tags.browse({
limit: 'all',
include: 'count.posts',
order: 'count.posts DESC',
});
// Автори
const authors = await ghostClient.authors.browse({
include: 'count.posts',
});
// Налаштування сайту
const settings = await ghostClient.settings.browse();
Інтеграція Next.js App Router
// app/blog/page.tsx
import { ghostClient } from '@/lib/ghost';
export const revalidate = 3600;
export default async function BlogPage() {
const posts = await ghostClient.posts.browse({
limit: 12,
include: ['tags', 'authors'],
});
return <PostGrid posts={posts} />;
}
// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
const posts = await ghostClient.posts.browse({ limit: 'all', fields: 'slug' });
return posts.map((post) => ({ slug: post.slug }));
}
export default async function PostPage({ params }) {
const post = await ghostClient.posts.read(
{ slug: params.slug },
{ include: ['tags', 'authors'] }
);
return <PostDetail post={post} />;
}
Рендеринг Ghost HTML
Ghost повертає готовий HTML в полі html. Для безпечного рендера та стилізації:
// components/GhostContent.tsx
import DOMPurify from 'isomorphic-dompurify';
export function GhostContent({ html }: { html: string }) {
const clean = DOMPurify.sanitize(html, {
ADD_TAGS: ['iframe'],
ADD_ATTR: ['allowfullscreen', 'frameborder'],
});
return (
<div
className="ghost-content prose prose-lg max-w-none"
dangerouslySetInnerHTML={{ __html: clean }}
/>
);
}
CSS для Ghost card-елементів потрібно підключити окремо:
// app/layout.tsx
import '@/styles/ghost-cards.css'; // стилі для kg-bookmark, kg-gallery, kg-video, etc.
Members API для headless
Members-функції (paywall, підписки) в headless-режимі вимагають додаткової роботи. Ghost надає JS SDK @tryghost/portal як iframe-віджет — можна вбудувати в будь-який фронтенд:
// Підключаємо Portal script
<script
src="https://myblog.com/public/member-attribution.min.js"
async
/>
// Відкриття форми входу/реєстрації
<button onClick={() => window.location.href = 'https://myblog.com/#/portal/signup'}>
Підписатися
</button>
Повнофункціональна headless-інтеграція з Members вимагає проксирування сесій через Ghost — це складніше за нативні теми й зазвичай не виправдано для паблішинг-проектів.
Терміни інтеграції
| Фронтенд | Завдання | Час |
|---|---|---|
| Next.js | Базовий блог (список + пост) | 1–2 дні |
| Next.js | Повний сайт (теги, автори, пошук) | 3–5 днів |
| Astro | Статичний сайт-блог | 1–2 дні |
| Gatsby | З Source Plugin | 1–2 дні |







