Architecture 15 мин чтения

WordPress Headless CMS + Next.js

Используем WordPress как headless CMS с Next.js на фронтенде: скорость статических сайтов + удобство WordPress-редактора.

Headless WordPress — WordPress отвечает только за контент (backend), а фронтенд — отдельное приложение на Next.js через API.

Зачем Headless

  • Скорость — Next.js генерирует статический HTML
  • SEO — SSG/SSR страницы индексируются отлично
  • Гибкость — любой дизайн, не ограниченный темами WP
  • Безопасность — WordPress не доступен напрямую

Архитектура

WordPress (редактор, БД, медиа) → REST API → Next.js (SSG/ISR страницы).

REST API: базовые запросы

// lib/wordpress.js
const WP_URL = process.env.NEXT_PUBLIC_WP_URL;

export async function getPosts(params = {}) {
  const query = new URLSearchParams({ per_page: 10, _embed: true, ...params });
  const res = await fetch(`${WP_URL}/wp-json/wp/v2/posts?${query}`, {
    next: { revalidate: 60 },
  });
  return res.json();
}

export async function getPost(slug) {
  const res = await fetch(`${WP_URL}/wp-json/wp/v2/posts?slug=${slug}&_embed`, {
    next: { revalidate: 3600 },
  });
  const posts = await res.json();
  return posts[0] ?? null;
}

Страница и динамические маршруты

// app/blog/[slug]/page.jsx
export async function generateStaticParams() {
  const posts = await getPosts({ per_page: 100 });
  return posts.map((post) => ({ slug: post.slug }));
}

export default async function PostPage({ params }) {
  const post = await getPost(params.slug);
  if (!post) return notFound();

  return (
    <article>
      <h1 dangerouslySetInnerHTML={{ __html: post.title.rendered }} />
      <div
        className="prose"
        dangerouslySetInnerHTML={{ __html: post.content.rendered }}
      />
    </article>
  );
}

On-Demand Revalidation

При публикации в WordPress — автоматически обновляем кеш Next.js:

// functions.php WordPress
add_action('save_post', function($post_id) {
    if (wp_is_post_revision($post_id)) return;
    $slug = get_post_field('post_name', $post_id);
    wp_remote_post('https://nextjs.example.com/api/revalidate', [
        'headers' => [
            'x-wp-secret' => WP_SECRET,
            'Content-Type' => 'application/json'
        ],
        'body' => json_encode(['post_slug' => $slug]),
    ]);
});
// app/api/revalidate/route.js (Next.js)
export async function POST(request) {
  const secret = request.headers.get('x-wp-secret');
  if (secret !== process.env.WP_SECRET) {
    return Response.json({ error: 'Unauthorized' }, { status: 401 });
  }
  const { post_slug } = await request.json();
  await revalidatePath(`/blog/${post_slug}`);
  await revalidatePath('/blog');
  return Response.json({ revalidated: true });
}

WPGraphQL — альтернатива REST API

Установите плагин WPGraphQL в WordPress и получайте только нужные поля:

export async function getAllPosts() {
  const res = await fetch(process.env.WP_GRAPHQL_URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      query: `{
        posts(first: 100) {
          nodes {
            id slug title excerpt date
            featuredImage { node { sourceUrl altText } }
            categories { nodes { name slug } }
          }
        }
      }`,
    }),
  });
  const { data } = await res.json();
  return data.posts.nodes;
}

Деплой

  • WordPress: VPS / Coolify / любой PHP-хостинг
  • Next.js: Vercel, Cloudflare Pages, или свой Node.js сервер
  • Связь: WordPress REST API должен быть доступен из Next.js