WordPress REST APIでヘッドレスCMS構築:Next.jsフロントエンドと連携する実践テクニック

WordPressをヘッドレスCMSとして使い、フロントエンドをNext.jsで構築するアーキテクチャが注目されています。コンテンツ管理はWordPressの使いやすい管理画面で、表示はモダンなReactアプリで、という両方の良いところを取る手法です。

WordPress REST APIの基本

WordPress 4.7以降、REST APIが標準搭載されています。/wp-json/wp/v2/ 以下のエンドポイントで投稿・ページ・カテゴリなどのデータをJSON形式で取得できます。

# 投稿一覧を取得
curl https://your-site.com/wp-json/wp/v2/posts

# 特定の投稿を取得
curl https://your-site.com/wp-json/wp/v2/posts/123

# カテゴリ一覧
curl https://your-site.com/wp-json/wp/v2/categories

# 検索
curl "https://your-site.com/wp-json/wp/v2/posts?search=React"

# ページネーション
curl "https://your-site.com/wp-json/wp/v2/posts?per_page=10&page=2"

Next.jsプロジェクトのセットアップ

npx create-next-app@latest wp-frontend --typescript --app
cd wp-frontend

WordPress APIクライアント

// lib/wordpress.ts
const API_URL = process.env.WORDPRESS_API_URL || "https://your-site.com/wp-json/wp/v2";

export interface WPPost {
  id: number;
  slug: string;
  title: { rendered: string };
  content: { rendered: string };
  excerpt: { rendered: string };
  date: string;
  categories: number[];
  _embedded?: {
    "wp:featuredmedia"?: Array<{ source_url: string }>;
  };
}

export async function getPosts(page = 1, perPage = 10): Promise<WPPost[]> {
  const res = await fetch(
    `${API_URL}/posts?_embed&per_page=${perPage}&page=${page}`,
    { next: { revalidate: 3600 } } // ISR: 1時間キャッシュ
  );
  if (!res.ok) throw new Error("Failed to fetch posts");
  return res.json();
}

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

export async function getCategories() {
  const res = await fetch(`${API_URL}/categories`);
  return res.json();
}

投稿一覧ページ

// app/page.tsx
import { getPosts } from "@/lib/wordpress";
import Link from "next/link";

export default async function Home() {
  const posts = await getPosts();

  return (
    <main>
      <h1>ブログ</h1>
      {posts.map((post) => (
        <article key={post.id}>
          <Link href={`/posts/${post.slug}`}>
            <h2 dangerouslySetInnerHTML={{ __html: post.title.rendered }} />
          </Link>
          <div dangerouslySetInnerHTML={{ __html: post.excerpt.rendered }} />
          <time>{new Date(post.date).toLocaleDateString("ja-JP")}</time>
        </article>
      ))}
    </main>
  );
}

投稿詳細ページ

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

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

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

  return (
    <article>
      <h1 dangerouslySetInnerHTML={{ __html: post.title.rendered }} />
      <time>{new Date(post.date).toLocaleDateString("ja-JP")}</time>
      <div dangerouslySetInnerHTML={{ __html: post.content.rendered }} />
    </article>
  );
}

カスタムエンドポイントの追加

functions.phpでカスタムREST APIエンドポイントを追加できます。人気記事ランキングや関連記事など、標準APIにない機能を実装する場合に使います。

// functions.php
add_action("rest_api_init", function() {
    register_rest_route("custom/v1", "/popular", [
        "methods" => "GET",
        "callback" => function() {
            $posts = get_posts([
                "meta_key" => "post_views",
                "orderby" => "meta_value_num",
                "order" => "DESC",
                "numberposts" => 5,
            ]);
            return array_map(function($p) {
                return [
                    "id" => $p->ID,
                    "title" => $p->post_title,
                    "slug" => $p->post_name,
                    "views" => get_post_meta($p->ID, "post_views", true),
                ];
            }, $posts);
        },
        "permission_callback" => "__return_true",
    ]);
});

CORS設定

ヘッドレス構成ではフロントエンドとWordPressが別ドメインになるため、CORS設定が必要です。

// functions.php
add_action("rest_api_init", function() {
    remove_filter("rest_pre_serve_request", "rest_send_cors_headers");
    add_filter("rest_pre_serve_request", function($value) {
        header("Access-Control-Allow-Origin: https://your-frontend.com");
        header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
        header("Access-Control-Allow-Headers: Content-Type, Authorization");
        return $value;
    });
});

まとめ

WordPressのREST APIとNext.jsを組み合わせることで、編集者にはWordPressの使いやすいUIを、ユーザーにはReactベースの高速な表示体験を提供できます。ISRを使えばビルド不要で最新コンテンツが反映される、最強のブログ基盤が構築できます。

コメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

IP: 取得中...
216.73.216.31216.73.216.31