Contentful 「相互参照」徹底解説:Reference フィールドによる柔軟なデータ設計と運用の実践例

Contentful 「相互参照」徹底解説:Reference フィールドによる柔軟なデータ設計と運用の実践例

Contentful の Reference フィールドを活用した柔軟なデータ設計・API連携の実践ガイド。著者・カテゴリ・関連記事などのリファレンス設計から、大規模運用に向けた拡張機能の活用まで、実践的なノウハウを体系的にまとめます。

概要

本記事では、Contentful の相互参照(Reference フィールド)を活用した、柔軟なデータ構造の実装方法と実践例を解説します。シングルスペース内での基本的な相互参照の設計から、GraphQL API を用いた参照データの取得、フロントエンドでの展開までを、サンプルコードとともに詳しく解説します。さらに、必要に応じてクロススペース参照(Cross-space references)の最新事情にも触れ、より大規模な設計を志向する方への指針も示します。

Reference フィールドとは

Contentful は API ファーストなヘッドレス CMS として、複数のコンテンツモデルを、「Reference フィールド」を用いて有機的につなぐことができます。 Reference フィールドは、他のエントリーやアセットを「フィールド」として参照できる機能です。これにより、著者・カテゴリ・関連投稿といったデータを一元的に管理しやすくなり複雑な CMS 要件にもスケーラブルに対応可能となります。

対象読者

  • Contentful を用いたデータ構造設計や、柔軟なコンテンツ管理に興味のあるエンジニア/ディレクター
  • 複数モデル間の「参照」をどのように実装・運用できるか知りたい方
  • GraphQL/REST API でリファレンス展開を実践したい方
  • クロススペース参照による「大規模運用」「多言語・多ブランド統合」に関心がある方

本記事のゴール

  • Contentful の Referenceフィールド(相互参照) の設計と作成方法を理解する
  • 参照先データを GraphQL API で展開・取得する方法を体験する
  • Next.js 等のフロントエンドからリファレンスを利用したデータ表示を実装できる
  • シンプルなサンプルコード・管理画面例をもとに一連の手順を追える

想定ユースケース

  • 記事コンテンツに「著者情報」「カテゴリ」「関連投稿」「タグ」などを別モデルで管理・再利用したい場合
  • EC サイトで「商品」「ブランド」「シリーズ」「カテゴリ」などを動的にひも付けたい場合
  • 多言語/多ブランド展開時に、スペースをまたいだ「クロススペース参照」を活用したい場合

技術選定と全体フロー

本記事では、下記の流れで実装例を構成します。

  1. Contentful 管理画面で Reference フィールドを設計
  2. 参照フィールド付きエントリーを作成
  3. Contentful GraphQL API で参照データをネスト取得
  4. Next.js(または Node.js)からデータを取得・展開

参考

実践:Contentful における相互参照フィールドの作成とデータ取得

1. Contentful 管理画面で Reference フィールドを設計する

1-1. コンテンツモデルを準備する

まず、相互参照の対象となるコンテンツタイプ(モデル) を複数用意します。例として、BlogPostAuthor の 2 つのモデルを作成します。

  • BlogPost(記事): title, body, author(Reference)
    BlogPost
  • Author(著者): name
    Author

1-2. エントリー作成時の参照指定

BlogPost エントリー作成時に、「author」フィールドで Author エントリーを検索・選択して参照を設定します。カテゴリや関連投稿など、複数参照も同様の手順で設定できます。

エントリー作成画面

Reference を設定後、下記のようになります。
Reference 設定後

2. GraphQL API で相互参照データを取得する

Contentful の Reference フィールドは、API 経由でネストして展開できます。ここでは GraphQL API を例に、記事エントリーと著者情報をまとめて取得するクエリ例を示します。

2-1. GraphQL エンドポイント

https://graphql.contentful.com/content/v1/spaces/{SPACE_ID}/environments/{ENVIRONMENT_ID}

2-2. BlogPost → Author の参照

クエリ

query {
  blogPostCollection(limit: 1, order: sys_publishedAt_DESC) {
    items {
      author {
        ... on Author {
          name
        }
      }
    }
  }
}

レスポンス

{
  "data": {
    "blogPostCollection": {
      "items": [
        {
          "author": {
            "name": "Koshii Takumi"
          }
        }
      ]
    }
  }
}

2-3. Author → BlogPost の参照

クエリ

query {
  authorCollection(limit: 1, order: sys_publishedAt_DESC) {
    items {
      name
      linkedFrom {
        entryCollection (limit: 1) {
          items {
            ... on BlogPost {
              title
            }
          }
        }
      }
    }
  }
}

レスポンス

{
  "data": {
    "authorCollection": {
      "items": [
        {
          "name": "Koshii Takumi",
          "linkedFrom": {
            "entryCollection": {
              "items": [
                {
                  "title": "相互参照のテスト投稿"
                }
              ]
            }
          }
        }
      ]
    }
  }
}

3. Next.js(Node.js)からのデータ取得・表示サンプル

Next.js のサーバーコンポーネントから fetch する場合の簡易例です。

// app/contentful-reference/page.tsx(App Router 例)
import fetch from 'node-fetch';

const SPACE_ID = 'your_space_id';
const ENVIRONMENT = 'master';
const ACCESS_TOKEN = 'your_contentful_access_token';

const endpoint = `https://graphql.contentful.com/content/v1/spaces/${SPACE_ID}/environments/${ENVIRONMENT}`;
const query = `
  query {
    blogPostCollection(limit: 1, order: sys_publishedAt_DESC) {
      items {
        title
        author {
          ... on Author {
            name
          }
        }
      }
    }
  }
`;

async function fetchPost() {
    const res = await fetch(endpoint, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${ACCESS_TOKEN}`,
        },
        body: JSON.stringify({ query }),
        cache: "no-store", // ISR/SSGでなくサーバーで毎回取得したい場合
    });
    const data = await res.json();
    return data.data.blogPostCollection.items[0];
}

// React component (App Router対応)
export default async function ContentfulReferencePage() {
    const post = await fetchPost();

    return (
        <main style={{ padding: 32 }}>
            <h1>{post?.title}</h1>
            <p>
                著者: {post?.author?.name ?? "著者情報なし"}
            </p>
        </main>
    );
}

ブラウザでのプレビューは以下の通りです。

プレビュー画面

4. Reference フィールド設計のベストプラクティスと注意点

  • 参照の深さ(REST API の include、GraphQL のネスト)は最大 10 階層まで
  • 循環参照(A が B を参照し、B が A を参照する)は設計時に注意
  • フィールド名・用途を明確に:著者、カテゴリ、関連投稿など用途ごとに適切に分ける
  • パフォーマンス:参照数や取得階層が深くなりすぎないよう注意
  • 複数モデルの参照:Reference フィールドで複数のコンテンツタイプを指定可能(例:記事が「著者」か「ゲスト寄稿者」を参照可)

発展:Compose およびクロススペース参照による複雑な運用・設計の拡張

Contentful の Compose(旧 Orchestration)やクロススペース参照を利用することで、より大規模で複雑なCMS構成や多ブランド運用にも対応できます。

  • Compose(旧 Orchestration)

  • クロススペース参照(Cross-space references)

    • 異なるスペース間でエントリーやアセットを相互参照可能
    • 多言語・多ブランド展開や大規模サイトの統合管理に有効
    • 利用時はスペース間の権限設計やAPIクエリの最適化に注意
    • Contentful公式: Cross-space references

本格的な多拠点運用や複雑な CMS 統合を目指す場合は、これらの機能の検討もぜひおすすめします。

まとめ

Contentful の Reference フィールドは、著者やカテゴリ、関連記事など複数のコンテンツ同士を柔軟かつ安全につなげる設計・運用を可能にします。GraphQL API などの活用により、これらのリファレンス情報をフロントエンドで簡単に展開でき、スケーラブルな CMS 運用・データ再利用にも大きな強みを発揮します。

さらに、Compose やクロススペース参照といった発展機能を活用すれば、多拠点・多ブランド・多言語運用など大規模サイトにも対応できる拡張性があります。

運用現場に応じて Reference フィールドをどう設計・活用するかが、Contentful の CMS 設計を成功させる鍵です。本記事がみなさんのプロダクト開発・業務設計の参考になれば幸いです。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.