コンテンツにスキップ

WordPress CMS統合ガイド

このドキュメントは、Next.js StorefrontとWordPress CMSの統合方法、アーキテクチャ、実装パターンを説明します。

概要

このプロジェクトでは、WordPressをHeadless CMSとして使用し、以下のコンテンツを管理します:

  • お知らせ(Announcements): サイト全体のお知らせ、重要なお知らせの固定表示
  • キャンペーン(Campaigns): プロモーションキャンペーン情報
  • Heroスライド(Hero Slides): ホームページのヒーローセクション用スライド

Next.js Storefrontは、WordPressのGraphQL API(WPGraphQL)を通じてコンテンツを取得し、サーバーサイドレンダリング(SSR)で表示します。

アーキテクチャ

┌─────────────────┐
│ WordPress CMS   │
│ (Headless)      │
│                 │
│ - Announcements │
│ - Campaigns     │
│ - Hero Slides   │
└────────┬────────┘
         │
         │ GraphQL API
         │ (WPGraphQL)
         │
         ▼
┌─────────────────┐
│ Next.js         │
│ Storefront      │
│                 │
│ - SSR Fetch     │
│ - Components    │
│ - Display       │
└─────────────────┘

データフロー

  1. コンテンツ作成: WordPress管理画面でコンテンツを作成・編集
  2. GraphQL API: WPGraphQLプラグインがGraphQLエンドポイントを提供
  3. Next.js Fetch: StorefrontがサーバーサイドでGraphQL APIを呼び出し
  4. 表示: 取得したデータをReactコンポーネントで表示

設定

環境変数

Next.js Storefrontでは以下のルールでエンドポイントを解決します。

  1. サーバー実行文脈(SSR/Route Handler等): WORDPRESS_GRAPHQL_ENDPOINT (例: https://wp.example.com/graphql)を最優先で参照し、WordPress本番ドメインへ直接アクセスする。
  2. クライアント実行文脈: WordPress の実ドメインを公開しないためプロキシ /api/wordpress/graphql を使用する。プロキシは apps/storefront/src/app/api/wordpress/graphql/route.ts で実装されており、storefrontConfig からは siteUrl + /api/wordpress/graphql の絶対URLとして参照される。

結果として、秘密情報は環境変数に閉じ込めつつ、クライアントは統一したパスでアクセスできます。

設定ファイル

設定は apps/storefront/src/lib/config.ts で管理されています:

export const storefrontConfig = {
  wordpressGraphqlEndpoint:
    typeof window === 'undefined'
      ? (process.env.WORDPRESS_GRAPHQL_ENDPOINT ??
        new URL('/api/wordpress/graphql', siteUrl).toString())
      : new URL('/api/wordpress/graphql', siteUrl).toString(),
  // ...(Vendure関連設定など)
};

※実装は apps/storefront/src/lib/config.ts にあり、プロキシ/直接アクセスの切り替えが自動化されています。

実装パターン

GraphQLクライアント

WordPress GraphQL APIへのリクエストは apps/storefront/src/lib/wordpress/client.tsfetchWordPressGraphQL 関数を使用します。

import { fetchWordPressGraphQL } from '@/lib/wordpress/client';

const data = await fetchWordPressGraphQL<QueryResult>({
  query: MY_QUERY,
  variables: { first: 10 },
  cache: 'no-store', // キャッシュ設定
  allowPartialData: true, // エラーがあってもdataを返す
});

コンテンツタイプ別の実装

お知らせ(Announcements)

ファイル: apps/storefront/src/lib/wordpress/announcements.ts

主な機能:

  • fetchAnnouncements(): お知らせ一覧を取得
  • fetchFeaturedAnnouncements(): フィーチャー済み(isFeatured: true)のお知らせを取得

使用例:

import {
  fetchAnnouncements,
  fetchFeaturedAnnouncements,
} from '@/lib/wordpress/announcements';

// 通常のお知らせ一覧
const announcements = await fetchAnnouncements({ limit: 10 });

// フィーチャー済みお知らせ(サイト上部に固定表示)
const featured = await fetchFeaturedAnnouncements({ limit: 5 });

コンポーネント:

  • FeaturedAnnouncementBanner: フィーチャー済みお知らせをサイト上部に表示

  • AnnouncementSection: お知らせ一覧セクション

キャンペーン(Campaigns)

ファイル: apps/storefront/src/lib/wordpress/campaigns.ts

主な機能:

  • fetchCampaigns(): キャンペーン一覧を取得(ACFのorderフィールドでソート)

使用例:

import { fetchCampaigns } from '@/lib/wordpress/campaigns';

const campaigns = await fetchCampaigns({ limit: 10 });

コンポーネント:

  • CampaignBannerSection: キャンペーンバナー一覧表示

Heroスライド(Hero Slides)

ファイル: apps/storefront/src/lib/wordpress/hero-slides.ts

主な機能:

  • fetchHeroSlides(): Heroスライド一覧を取得(ACFのorderフィールドでソート)

使用例:

import { fetchHeroSlides } from '@/lib/wordpress/hero-slides';

const heroSlides = await fetchHeroSlides({ limit: 10 });

コンポーネント:

  • HomeHero: ホームページのHeroセクション(カルーセル表示)

エラーハンドリング

フォールバッククエリ

各コンテンツタイプは、メインクエリが失敗した場合にフォールバッククエリを実行します:

// メインクエリ(announcements)
try {
  const data = await fetchWordPressGraphQL({ query: ANNOUNCEMENT_QUERY });
  // ...
} catch (error) {
  // フォールバッククエリ(contentNodes)
  const fallback = await fetchWordPressGraphQL({
    query: ANNOUNCEMENT_FALLBACK_QUERY,
  });
  // ...
}

部分データの許可

allowPartialData: true を設定すると、GraphQLエラーがあってもdataが存在する場合はデータを返します:

const data = await fetchWordPressGraphQL({
  query: MY_QUERY,
  allowPartialData: true, // エラーがあってもdataを返す
});

サーバーサイドレンダリング(SSR)

すべてのWordPressコンテンツはサーバーサイドで取得されます:

ページコンポーネント

// app/page.tsx
export default async function HomePage() {
  const announcements = await fetchAnnouncements({ limit: 10 });
  const heroSlides = await fetchHeroSlides({ limit: 10 });
  const campaigns = await fetchCampaigns({ limit: 10 });

  return (
    <HomePageClient
      initialAnnouncements={announcements}
      initialHeroSlides={heroSlides}
      initialCampaigns={campaigns}
    />
  );
}

レイアウトコンポーネント

// app/layout.tsx
export default async function RootLayout({ children }: RootLayoutProps) {
  const featuredAnnouncements = await fetchFeaturedAnnouncements({ limit: 5 });

  return (

    <html>
      <body>
        {featuredAnnouncements.length > 0 && (
          <FeaturedAnnouncementBanner announcements={featuredAnnouncements} />
        )}
        {/* ... */}

      </body>
    </html>
  );
}

キャッシュ戦略

現時点では、WordPressコンテンツはキャッシュしない設定(cache: 'no-store')になっています:

  • 理由: コンテンツ更新を即座に反映するため
  • 将来の改善: ISR(Incremental Static Regeneration)やrevalidate設定を検討

コンテンツモデル

お知らせ(Announcement)

WordPress投稿タイプ: announcement

標準フィールド:

  • title: タイトル
  • date: 公開日

  • slug: スラッグ

  • link: リンクURL

ACFフィールド (announcementMeta):

  • isFeatured: フィーチャー済みフラグ(boolean)

  • relatedLink: 関連リンク(string)

キャンペーン(Campaign)

WordPress投稿タイプ: campaign

標準フィールド:

  • title: タイトル
  • date: 公開日
  • slug: スラッグ
  • link: リンクURL
  • featuredImage: アイキャッチ画像

ACFフィールド (campaignMeta):

  • order: 表示順序(number)
  • image: キャンペーン画像(MediaItem)

Heroスライド(Hero Slide)

WordPress投稿タイプ: hero_slide

標準フィールド:

  • title: タイトル
  • date: 公開日
  • slug: スラッグ
  • link: リンクURL

ACFフィールド (heroSlideMeta):

  • order: 表示順序(number)
  • media: メディア(image/video)
  • type: メディアタイプ('image' | 'video')
  • image: 画像(desktop/mobile対応)
  • video: 動画(desktop/mobile対応)
  • alt: altテキスト
  • link: リンク先(Campaign/Announcement)

トラブルシューティング

GraphQLエラーが発生する

  1. エンドポイントが正しいか確認
curl http://localhost:8181/graphql -X POST -H "Content-Type: application/json" -d '{"query":"{ __typename }"}'
  1. WPGraphQLプラグインが有効化されているか確認
docker compose run --rm wp-cli plugin list
  1. ACFフィールドがGraphQLに公開されているか確認
  2. WordPress管理画面 → Custom Fields → フィールドグループ → GraphQL設定

コンテンツが表示されない

  1. 投稿が公開状態か確認
  2. GraphQLスキーマに投稿タイプが登録されているか確認
query {
  __schema {
    types {
      name
    }
  }
}
  1. ブラウザの開発者ツールでネットワークエラーを確認
  2. サーバーログでエラーを確認

画像が表示されない

  1. Next.jsの画像設定を確認next.config.js
  2. remotePatternsにWordPressドメインが登録されているか確認
  3. 画像URLが正しいか確認

参考資料