Eyes, JAPAN Blog > 静的サイトに載せるお知らせをHeadless CMSで管理する

静的サイトに載せるお知らせをHeadless CMSで管理する

shimizu

この記事は1年以上前に書かれたもので、内容が古い可能性がありますのでご注意ください。

今回は「Headless CMS」と呼ばれるシステムを使って、静的なWebサイトに動的なコンテンツを掲載する例を紹介します。

CMSと言えば、WordPressに代表されるように、Webサイトに掲載する記事やページなどの文章や画像といったコンテンツの編集や、サイトの構造や外観を定義するテンプレートやテーマを管理するシステムで、Webメディアや企業サイト、オンラインショップサイトなどに幅広く利用されています。

従来型のCMSでは、コンテンツの管理とそれをブラウザに画面表示させるためのフロントエンドシステムが一体となっている構造でした。これに対して、コンテンツの管理の機能のみを提供するCMSが「Headless CMS」です。Headless CMSはコンテンツを画面表示できる形にレンダリングするフロントエンドシステムを持たず、コンテンツの情報をAPIで外部のシステムに提供します。そうすることで、以下のメリットが生まれます。

  • マネージドなHeadless CMSサービスを使うことで、WordPressなどCMS自体の構築・運用管理(セキュリティー更新など)が不要になる
  • 従来の一体型CMSと比較し、表示処理とコンテンツを分離することができる
  • 従来の一体型CMSと比較し、表示の高速化や負荷耐性向上を見込める
  • 一つのコンテンツを、Webだけでなく複数の媒体で利用しやすくなる(Web、モバイルアプリ、バックエンドなど)
  • 以上により作業効率が向上する

今回は、企業サイトのページに新着情報を掲載するという想定で、Headless CMSを用いて静的なWebサイトに動的なコンテンツを埋め込む方法を試してみましょう。

Headless CMSの環境をセットアップする

既存のHeadless CMSには、複数のサービスがあります。どんなものがあるのかについては、以下の記事などを参照してください。

Headless CMS 軽く触って比較してみた(Contentful / microCMS / strapi / GraphCMS) – Qiita
https://qiita.com/cheez921/items/81cba28e4b815709f863

今回は、管理画面のUIが日本語表記に対応しているMicroCMSを利用します。

MicroCMSのサイトを開き、「新規作成」ボタンからアカウントを作成します。

アカウント作成が完了すると、コンテンツを管理する準備を行うステップが表示されます。

Webサイト1件が「サービス」1件に対応し、その中に「API」と呼ばれる枠(WordPressにおける「カスタム投稿タイプ」)を作ってデータ構造を定義し、それに対して記事を登録・編集して入れ込むという構造になっています。

まずは、管理項目の最上位の単位となる「サービス」を登録します。
この例では以下のように記入しました。

次に、「API」を作成します。ここでは以下のように記入しました。

続いてAPIが返戻する値の形式を選択します。ここでは「リスト形式」を選びました。

次に、重要となるデータ構造である「APIスキーマ」の定義に移ります。ここに、1件の記事にどのような情報を持たせるのかを設定できます。今回は以下のデータを定義しました。

  • title(タイトル)……記事の件名(一行テキスト型)
  • date(日付)……記事の投稿日(日時型。ただし「詳細設定」から「日付指定のみ」をオンにした)
  • body(本文)……記事の本文テキスト(複数行テキスト型)

これでデータ構造が用意できたので、「追加」ボタンを押して記事をいくつか作成します。この例では3つの記事を作成しました。

以上で、MicroCMS上でのコンテンツの準備ができました。

コンテンツを静的サイト上に掲載する

まず、APIのリクエスト先URLを確認しておきましょう。管理画面の「API設定」に入り、「エンドポイント」の欄で確認できます。

また、APIリクエストの際に使う認証キーは、管理画面のサービス設定画面で確認できます。

それでは、WebページからMicroCMS上のコンテンツを取得して表示してみましょう。次のHTMLファイルを用意しました。

(APIのリクエスト先URLと認証キーは、それぞれ上記で確認したものに置き換えてください)

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>Example Corporation</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script src="https://unpkg.com/[email protected]/dayjs.min.js"></script>

    <style>
      html {
        background: url(https://source.unsplash.com/featured/?nature,forest);
        background-size: cover;
      }
      body {
        background-color: rgb(255, 255, 255, 0.9);
        margin: 2rem;
        padding: 2rem;
      }
    </style>
  </head>
  <body>
    <h1>Example Corporation</h1>

    <h2>新着情報</h2>

    <div id="app">
      <ul>
        <li v-for="item in newsItems">
          <h3>{{ item.date }} {{ item.title }}</h3>
          <p v-html="item.body"></p>
        </li>
      </ul>
    </div>

    <script>
      const app = new Vue({
        el: '#app',
        data: {
          newsItems: []
        }
      });

      // 新着情報のコンテンツを取得するAPIリクエストを行う
      fetch('https://example-web.microcms.io/api/v1/news', {
        method: 'GET',
        headers: new Headers({
          'X-API-KEY': 'cce850f8-2212-adeb-b65f-a5c18655ebdd'
        })
      })
        .then((response) => response.json())
        .then((data) => {
          // APIで取得したコンテンツ一覧を取り出す
          const newsItems = data.contents;

          // 日付の値を整形する
          newsItems.forEach((item) => {
            item.date = dayjs(item.date).format('YYYY/MM/DD');
          });

          // 完成したコンテンツ一覧をセットする
          app.newsItems = newsItems;
        }).catch((error) => {
          console.log(error);
        });
    </script>
  </body>
</html>

コンテンツの取得は、後半部分のJavaScriptコードで行っています。このとき、公式APIドキュメントの仕様に従って、認証キーの値をリクエストヘッダーの「X-API-KEY」に設定します。

(fetch()の使い方については Fetch の使用 – Web API | MDN などを参照してください)

      ︙
      // 新着情報のコンテンツを取得するAPIリクエストを行う
      fetch('https://example-web.microcms.io/api/v1/news', {
        method: 'GET',
        headers: new Headers({
          'X-API-KEY': 'cce850f8-2212-adeb-b65f-a5c18655ebdd'
        })
      })
      ︙

リクエストが正常に処理されると、結果がレスポンスされます。返ってきたJSONデータからコンテンツ一覧の配列を取り出し、画面表示のための整形をします。

        ︙
        .then((data) => {
          // APIで取得したコンテンツ一覧を取り出す
          const newsItems = data.contents;

          // 日付の値を整形する
          newsItems.forEach((item) => {
            item.date = dayjs(item.date).format('YYYY/MM/DD');
          });

          // 完成したコンテンツ一覧をセットする
          app.newsItems = newsItems;
        }) ...
        ︙

最終的に、Vue.jsが下記の部分のテンプレートを展開し、各記事がレンダリングされ画面に現れます。

    ︙
    <div id="app">
      <ul>
        <li v-for="item in newsItems">
          <h3>{{ item.date }} {{ item.title }}</h3>
          <p v-html="item.body"></p>
        </li>
      </ul>
    </div>
    ︙

利点・注意点について

今回のサンプルでは、静的なWebページの一部に動的管理コンテンツを入れ込むために外部サービスとしてのHeadless CMSを利用しましたが、この構成を取ることで自前でホスティングして公開するのは静的なサイト資材のみで済み、WordPressなどを動かすためのアプリケーションサーバーを構築したり運用管理する手間が不要となります。さらにアクセス数が増大する状況になっても負荷耐性が比較的高いメリットもあります。

一つ注意する点は、公式APIドキュメントでも

API-KEY / WRITE-API-KEYはサービス内の全API共通となります。

そのため、クライアントサイドから直接APIを呼び出すことでユーザーがキーを把握できてしまう場合、エンドポイントさえ分かれば他のAPIも呼び出せてしまうことにご注意ください。
対処法としては、サーバサイドからAPIを呼び出す、またはJamstack構成にするなどしてキーを漏洩しないことが挙げられます。

と言及されているとおり、JavaScriptコードに書き込まれた認証キーが公開状態にあり誰にでも見られる状態にあることです。「API-KEY」は読み取りリクエスト専用であり、コンテンツを書き換えたり削除することはできないにしても、各利用プランに定められたデータ転送量を意図的に消費させるなどのDoS行為に悪用される懸念はあります。

その点まで考慮する場合は、上記で挙げられているようにサーバーサイド処理で仲介(Function as a Serviceでのバックエンド処理など)するか、またはJamstack構成としコンテンツが変更される度に動的コンテンツの内容を反映して静的化するといった対応策が有効です。

まとめ

今回はHeadless CMSを使って、静的なWebサイトに動的なコンテンツを掲載する例を紹介しました。この他にも、Headless CMSで管理するコンテンツをモバイル機器にプッシュ通知するなど、柔軟な用途に活用できます。効率的なシステム開発や運用のための技術要素として取り入れていきたいですね。

Comments are closed.