Интеграция 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 и т.д.
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 дня |







