Интеграция WP REST API с фронтендом (React/Vue/Next.js)

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.
Разработка и обслуживание любых видов сайтов:
Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Интеграция WP REST API с фронтендом (React/Vue/Next.js)
Средняя
~3-5 рабочих дней
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1214
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    852
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    823
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    815

Интеграция WP REST API с фронтендом (React/Vue/Next.js)

WordPress как headless CMS — рабочий подход для проектов, где нужен нестандартный фронтенд: SPA, мобильное приложение, статический сайт с динамическими данными. WP REST API отдаёт данные, React/Vue/Next.js рендерит интерфейс. Разработка headless-интеграции от «чистого» Next.js до полноценного production — от 5 до 15 рабочих дней в зависимости от объёма контента и сложности маршрутизации.

Базовая интеграция: получение данных

WordPress REST API по умолчанию доступен по /wp-json/wp/v2/. Получение последних постов:

// lib/wordpress.ts
const WP_API_URL = process.env.NEXT_PUBLIC_WP_URL + '/wp-json/wp/v2';

export interface WPPost {
  id: number;
  slug: string;
  title: { rendered: string };
  content: { rendered: string };
  excerpt: { rendered: string };
  date: string;
  featured_media: number;
  _embedded?: {
    'wp:featuredmedia'?: [{ source_url: string; alt_text: string }];
    'wp:term'?: Array<Array<{ id: number; name: string; slug: string }>>;
  };
}

export async function getPosts(params: {
  perPage?: number;
  page?: number;
  category?: number;
  search?: string;
} = {}): Promise<{ posts: WPPost[]; total: number; totalPages: number }> {
  const qs = new URLSearchParams({
    per_page: String(params.perPage ?? 12),
    page:     String(params.page ?? 1),
    _embed:   'wp:featuredmedia,wp:term',
    ...(params.category && { categories: String(params.category) }),
    ...(params.search   && { search: params.search }),
  });

  const res = await fetch(`${WP_API_URL}/posts?${qs}`, {
    next: { revalidate: 60 }, // ISR в Next.js 13+
  });

  if (!res.ok) throw new Error(`WP API error: ${res.status}`);

  return {
    posts:      await res.json(),
    total:      Number(res.headers.get('X-WP-Total')),
    totalPages: Number(res.headers.get('X-WP-TotalPages')),
  };
}

export async function getPostBySlug(slug: string): Promise<WPPost | null> {
  const res = await fetch(`${WP_API_URL}/posts?slug=${slug}&_embed=wp:featuredmedia,wp:term`);
  const posts = await res.json();
  return posts.length ? posts[0] : null;
}

Next.js App Router: динамические маршруты

// app/blog/[slug]/page.tsx
import { getPostBySlug, getPosts } from '@/lib/wordpress';
import { notFound } from 'next/navigation';

export async function generateStaticParams() {
  const { posts } = await getPosts({ perPage: 100 });
  return posts.map(post => ({ slug: post.slug }));
}

export async function generateMetadata({ params }: { params: { slug: string } }) {
  const post = await getPostBySlug(params.slug);
  if (!post) return {};
  return {
    title:       post.title.rendered,
    description: post.excerpt.rendered.replace(/<[^>]+>/g, '').slice(0, 160),
  };
}

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

  const media = post._embedded?.['wp:featuredmedia']?.[0];

  return (
    <article className="post-single">
      {media && (
        <img
          src={media.source_url}
          alt={media.alt_text}
          className="post-single__cover"
        />
      )}
      <h1 dangerouslySetInnerHTML={{ __html: post.title.rendered }} />
      <div
        className="post-content"
        dangerouslySetInnerHTML={{ __html: post.content.rendered }}
      />
    </article>
  );
}

dangerouslySetInnerHTML здесь допустим — контент приходит от доверенного WordPress-сервера, но нужен DOMPurify если источник не полностью под вашим контролем.

Кастомный хук для React SPA

// hooks/usePosts.ts
import useSWR from 'swr';

const fetcher = (url: string) => fetch(url).then(r => r.json());

export function usePosts(category?: string, page = 1) {
  const params = new URLSearchParams({ per_page: '12', page: String(page), _embed: '1' });
  if (category) params.set('categories', category);

  const { data, error, isLoading } = useSWR<WPPost[]>(
    `/wp-json/wp/v2/posts?${params}`,
    fetcher,
    { revalidateOnFocus: false }
  );

  return { posts: data ?? [], isLoading, error };
}
function BlogList() {
  const [page, setPage] = useState(1);
  const { posts, isLoading } = usePosts(undefined, page);

  if (isLoading) return <PostsSkeleton />;

  return (
    <>
      <div className="posts-grid">
        {posts.map(post => <PostCard key={post.id} post={post} />)}
      </div>
      <button onClick={() => setPage(p => p + 1)}>Загрузить ещё</button>
    </>
  );
}

GraphQL через WPGraphQL

WPGraphQL — плагин, добавляющий GraphQL-эндпоинт. Для сложных страниц с вложенными данными GraphQL выгоднее REST: один запрос вместо нескольких:

query GetProjectWithRelated($slug: String!) {
  projectBy(slug: $slug) {
    id
    title
    content
    projectDetails {
      client
      year
      projectUrl
    }
    categories {
      nodes { name slug }
    }
    techStack {
      nodes { name slug }
    }
    featuredImage {
      node { sourceUrl altText }
    }
  }
}
async function getProject(slug: string) {
  const res = await fetch(process.env.WP_GRAPHQL_URL!, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ query: GET_PROJECT_QUERY, variables: { slug } }),
    next: { revalidate: 300 },
  });
  const { data } = await res.json();
  return data.projectBy;
}

On-demand ISR при публикации в WordPress

Next.js поддерживает on-demand revalidation — перестройку страниц при изменении данных в CMS:

// WordPress: при сохранении поста — пинг Next.js
add_action('save_post', function (int $post_id, WP_Post $post): void {
    if ($post->post_status !== 'publish') return;

    $next_url = get_option('nextjs_revalidate_url');
    $secret   = get_option('nextjs_revalidate_secret');

    if (!$next_url || !$secret) return;

    wp_remote_post("{$next_url}/api/revalidate", [
        'body'    => json_encode([
            'secret' => $secret,
            'path'   => '/' . $post->post_type . '/' . $post->post_name,
        ]),
        'headers' => ['Content-Type' => 'application/json'],
        'blocking'=> false, // не ждём ответа
    ]);
}, 10, 2);
// Next.js: app/api/revalidate/route.ts
import { revalidatePath } from 'next/cache';

export async function POST(req: Request) {
  const { secret, path } = await req.json();

  if (secret !== process.env.REVALIDATE_SECRET) {
    return Response.json({ error: 'Invalid secret' }, { status: 401 });
  }

  revalidatePath(path);
  return Response.json({ revalidated: true, path });
}

CORS для headless

WordPress и Next.js на разных доменах требуют CORS-настройки:

add_action('rest_api_init', function () {
    remove_filter('rest_pre_serve_request', 'rest_send_cors_headers');
    add_filter('rest_pre_serve_request', function ($value) {
        $origin = $_SERVER['HTTP_ORIGIN'] ?? '';
        $allowed = ['https://mysite.com', 'https://www.mysite.com', 'http://localhost:3000'];
        if (in_array($origin, $allowed)) {
            header('Access-Control-Allow-Origin: ' . $origin);
            header('Access-Control-Allow-Credentials: true');
            header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
            header('Access-Control-Allow-Headers: Authorization, Content-Type');
        }
        return $value;
    });
});

Производительность: что кешировать

Данные Стратегия
Список постов ISR, revalidate: 60s
Одиночный пост ISR + on-demand revalidate при save_post
Меню навигации Static (revalidate: false)
Поисковые результаты SSR (без кеша, параметры меняются)
ACF-поля настроек сайта Static или revalidate: 3600s

Headless WordPress — это архитектурный выбор, не просто настройка плагина. Фронтенд-разработка, настройка WordPress и деплой двух независимых приложений суммируются в весомый объём работы, который нужно закладывать в оценку заранее.