このページの本文へ

手を動かして学ぶCMSの新しい潮流「ヘッドレス」とは何か?

2017年08月24日 18時00分更新

文●Chris Erwin

  • この記事をはてなブックマークに追加
本文印刷
フロントエンドとバックエンドを分離する「ヘッドレスCMS」が話題となっています。Elemenoを例に、ヘッドレスCMSを使ったWebサイトの開発方法を説明します。

「ヘッドレスCMS」は比較的新しいタイプのツールです。特徴や、利用方法を解説します。

従来のCMSは、コンテンツを作って管理するバックエンドと、コンテンツを表示するフロントエンド、2つのコンポーネントがあります。フロントエンドは、昔からのテクニック、ツールやライブラリーで作られた「Webサイト」です。

ヘッドレスCMSは、コンテンツを制作して管理するバックエンドしかありません。ロバストなAPIでコンテンツを配信し、開発者はAPIを用いて簡単にコンテンツを管理できます。この役割分担は、CMSがコンテンツの管理しかやらないことを意味します。ヘッドレスCMSはコンテンツの表示は関知しません。任意のプログラム言語、フレームワーク、ライブラリーで、コンテンツをさまざまなデバイスやプラットホームに配信します。

Elemenoで簡単なポートフォリオを作る

Elemenoを使って簡単なポートフォリオサイトを作ります。Elemenoは人気のあるヘッドレスCMSで、私は共同開発者の1人です。Node.jsの開発スタックを使いますが、ほかの言語やフレームワークにも同じ原則で応用できます。

以下がツールに含まれます。

  • Node.js、サーバー側のJavaScriptランタイム環境
  • Express、Webフレームワーク
  • Pug、テンプレートライブラリー(以前は、Jadeと呼ばれていました)
  • Elemeno、ヘッドレスCMS

Elemeno上でプロジェクトのセットアップをします。Elemenoはホストされた製品で、インストールやセットアップが必要なデータベースはありません。アカウントがなければ、アカウントを作って新しいプロジェクトを始めます。開発プランなら、無料です。このデモは開発プランで十分です。

コンテンツモデルを定義する

次のステップは、コンテンツモデルの定義です。コンテンツモデルは、コンテンツを作るときに表示されるテンプレートです。Elemenoにはシングルとコレクション、2種類のコンテンツモデルがあります。コレクションは、同じコンテンツモデルからアイテムを複数作れます。製品、商品、プロジェクトのコレクションなどに最適です。シングルは、コンテンツモデルからアイテムを1つ作ります。コンタクト情報や、About usページなどにオススメです。

簡単なポートフォリオサイト作成のために、プロジェクトの情報を格納するコレクションを作ります。プロジェクトごとに、表示する情報、選んだ情報をキャプチャーするのに適しているタイプを決めます。これから作るポートフォリオで、各プロジェクトが持っている情報です。

  • タイトル(プレーンテキスト)
  • 記述(マークダウン)
  • 日付(日時)
  • スクリーンショット/写真(イメージピッカー)
  • URL(プレーンテキスト)

Elemenoでコレクションを作ります。スクリーンの最上部でコレクションの名前を決めます。ここでは「Portfolio」とします。スクリーンの左側に「title」入力、右側に入力要素のリストが表示されます。コンテンツモデルの定義は、リストから左側のブランクスレートに入力要素をドラッグ&ドロップします。ポートフォリオのコンテンツ項目を作るときとまったく同じです。

各入力要素を配置したあと、「入力」をクリックして、設定を変更します。各入力に「ラベル」を設定します。ほかはデフォルトのままにします。

ポートフォリオコレクションは以下の通りです。

Elemeno interface: Portfolio collection screen shot

「Save」ボタンをクリックしてコレクションを保存します。

アイテムを作成する

ポートフォリオのコレクションを定義したら、アイテムを作成します。「最初のアイテムを作る(Create the first item)」をクリックします。入力項目を埋めて、「ただちにセーブして発行(Save and Publish Now)」ボタンをクリックし、ポートフォリオアイテムを作成します。アイテムをいくつか追加して、ポートフォリオコレクションを埋めます。

これでコードを書いてサイトを作る準備ができました。開発環境にNode.jsとNPMがインストールされているか確認します。ターミナルを開いて次のコマンドを入力します。

node -v

インストールしたバージョンが現れるか、Nodeがインストールされていない場合、

command not found: node

と表示されます。インストールはNode.jsのWebサイトからインストーラをダウンロードします。

プロジェクトファイルを格納するディレクトリを作り、ターミナルウィンドウで作ったフォルダーに移動します。プロジェクトではnodeのパッケージがいくつか必要になるので、NPMで次のコマンドを使いインストールします。

npm install elemeno express pug

新しいファイル「server.js」を作り、ダウンロードしたパッケージをインクルードして開始します。

var Elemeno = require('elemeno'),
    express = require('express'),
    pug = require('pug');

Expressを呼と、新しいExpressアプリケーションが作成されます。

var app =express();

Pugテンプレートファイルをすべて置くディレクトリ「views」を作ります。テンプレートの場所や、利用するテンプレートエンジンを指定するため、Expressの設定をします。

注意:サポートされたテンプレートエンジンならどれでも使えます。好みに合わせて選んでください。

app.set('views', './views');
app.set('view engine', 'pug');

ランディングページへのルートを作ります。server.jsにルートをすべて定義して、分かりやすくします。最初のルートを定義します:

app.get('/', function(req, res) {
    res.render('index.pug');
});

誰かがルート/GETリクエストを出すと、index.pugテンプレートを実行するようにExpressに知らせます。

次のステップでは、viewsディレクトリ内にindex.pugテンプレートを作ります。新しいファイルを作り「index.pug」で保存します。ファイルの中身は、簡単なものにします。

h1 Hello World!

server.jsに戻り、Expressにどのサーバーポートを使うか設定します。

app.listen(3000, function () {
    console.log('Portfolio listening on port 3000');
});

ターミナルウィンドウから

node server

と入力して、エンターキーを押せば、Webサイトが立ち上がります。

超簡単なノードWebサイトができました。Webブラウザーで

localhost:3000

にアクセスすると、「Hello World!」が表示されます。

コンテンツを入力する

Elemenoからコンテンツを入力します。ポートフォリオWebサイトからコンテンツにアクセスできるようにするAPIキーを作成します。Elemenoの設定画面でAPIキーセクションを見つけ、「APIキーを作成」ボタンをクリックします。キーに「Website」と名前をつけます。ほかはデフォルトのままとします。新しく作ったAPIキーがリストに表示されます。クリックして詳細から「Access Token」の値をコピーします。

server.jsファイルに戻り、ファイルの先頭付近にElemenoクライアントの新しいインスタンスを作成します。

var Elemeno = require('elemeno'),
    express = require('express'),
    pug = require('pug');

var app = express();
var elemeno = new Elemeno('PASTE-ACCESS-TOKEN-HERE');

ElemenoでコピーしたAccessTokenを貼り付けます。インデックスルートに、ポートフォリオコレクションからプロジェクトのリストを取得します。

app.get('/', function(req, res) {
    elemeno.getCollectionItems('portfolio', function(err, response) {
        res.render('index.pug', {projects: response.data});
    });
});

ポートフォリオコレクションのスラグ「portfolio」をgetCollectionItems関数に渡します。この関数は、コレクション中のアイテムを代表するオブジェクト配列を返します。Pugテンプレートを実行し、返ってきたデータを渡します。

テンプレートにデータをインプリメントする

Pugテンプレートにデータをインプリメントします。index.pugテンプレートを開き、プロジェクトのリストを表示する簡単なforinループを作成します。

for project in projects
    h1= project.title
    div!= project.content.description.html
    img(src= project.content.screenshot.imageUrl)
    p: a(href= project.content.link) Visit Site

Pugテンプレートに慣れていないと、ちょっと変に感じるかもしれませんが、各行で、出力したいタグとそのタグのコンテンツを定義します。Elementoから返された各ポートフォリオ・オブジェクトのネストされた構造にアクセスするため、ドット記法を使います。ポートフォリオのアイテムは、次の構造に似た形をしています。

{
  "id": "123e4567-e89b-12d3-a456-426655440000",
  "slug": "google",
  "title": "Google",
  "dateCreated": "2017-07-11T14:28:49.772Z",
  "dateUpdated": "2017-07-11T14:28:49.772Z",
  "datePublished": "2017-07-11T14:28:49.772Z",
  "published": true,
  "content": {
    "description": {
      "markdown": "The best little search engine that ever existed",
      "html": "<p>The best little search engine that ever existed</p>"
    },
    "date": "2005-07-11T04:00:00+00:00",
    "link": "http://google.com",
    "screenshot": {
      "dateCreated": "2017-07-11T14:25:04.950Z",
      "dateUpdated": "2017-07-11T14:25:04.950Z",
      "title": "google.png",
      "fileSize": "65941",
      "mimeType": "image/png",
      "metaData": {
        "width": 580,
        "colors": {
          "palette": null,
          "dominant": {
            "rgb": {
              "b": 232,
              "g": 233,
              "r": 233
            },
            "hex": "E9E9E8",
            "isLight": true
          }
        },
        "height": 407,
        "alternativeText": null,
        "copyrightInformation": null
      },
      "tags": [],
      "imageUrl": "https://d1o9o9r2qlybfc.cloudfront.net/full/08c3f6dd-b6da-4241-bdd0-288799b1e1a4.png",
      "thumbnails": {}
    }
  }
}

Nodeサーバーが走っているターミナルウィンドウに戻って、キーボードでControl +Cを入力すると、サーバーが再スタートし、ノードサーバーが停止します。node serverと入力してエンターを押せば、サーバーが再スタートします。

次にプロジェクトの詳細ページを作り、コンテンツをホーム以外にも表示できるようにします。viewsディレクトリに新しいテンプレート「details.pug」を作ります。ファイルの中身は以下の通りです。

h1= project.title
div!= project.content.description.html
img(src= project.content.screenshot.imageUrl)
p: a(href= project.content.link) Visit Site

インデックステンプレートを更新して、詳細ビューにリンクを張り、プロジェクト名のみリストします:

for project in projects
    ul
        li: a(href='/view/' + project.slug) #{project.title}

詳細ページのURLにproject.slugが埋め込まれています。詳細ページがどのポートフォリオ項目を取るか設定できます。

詳細ビューのためにserver.jsファイルで、新しいルートを定義します。

app.get('/view/:slug', function(req, res) {
    var slug = req.params.slug;

    elemeno.getCollectionItem('portfolio', slug, function(err, response) {
        res.render('details.pug', {project: response.data});
    });
});

ルートパスをダイナミックパラメータ:slugで定義します。/views/ANYTHINGにGETリクエストを出すと、このルートで処理されます。URLからslugパラメータを取り出し、新しいパラメータを設定して、getCollectionItem関数に渡します。この関数は、コレクションから特異なアイテムを取るので、コレクションスラグとアイテムスラグを両方渡す必要があります。要求したアイテムを代表する単一のオブジェクトを返します。details.pugテンプレートを実行し、ポートフォリオアイテムにデータを渡します。

サーバーを再起動します。詳細ページにリンクしたページができて、Elemenoから引き出されたコンテンツが含まれます。Elemenoで新しいポートフォリオを作って、ポートフォリオのホームページを更新した場合、リストされた新しいプロジェクトも表示されます。

最後のステップは、ポートフォリオコレクションにないプロジェクトにアクセスしようとした場合の処理法です。サーバーがクラッシュしないように、エラーメッセージを表示する簡単なエラーハンドラを詳細ルートに作ります。

app.get('/view/:slug', function(req, res) {
    var slug = req.params.slug;

    elemeno.getCollectionItem('portfolio', slug, function(err, response) {
        if (err) {
            return res.render('404.pug');
        }

        res.render('details.pug', {project: response.data});
    });
});

エラーメッセージとともに、viewsディレクトリに404.pugテンプレートを作ります。

h1 Oops! that project does not exist

サーバーを再起動します。存在しないプロジェクトにアクセスすると、404ページが表示されます。

最後に

ヘッドレスCMSからコンテンツを取る簡単なWebサイトを作りました。デザインとユーザー体験に関しては、不満な点はたくさんありますが、ElemonoのようなヘッドレスCMSからコンテンツにアクセスする簡単さと、どんな言語、テンプレートエンジン、フレームワークでも使えると分かったと思います。

EJS、Dust、Handlebarsなど別のテンプレートエンジンや、Expressのかわりに、Koa、Hapi、Sailsなどのフレームワーク、Nodeのかわりに、Ruby、PHP、Pythonなど別のプログラム言語も使えます。Webサイトのかわりにモバイルアプリ、ウォッチアプリ、そのほかArduinoのプロトタイプなども作れます。

ヘッドレスCMSなら、お気に入りのツールやテクニックを使って複数のデバイスでプロジェクトが作れて、コンテンツの管理が簡単になります。

ポートフォリオサイトの完全バージョンをGitHub上で共有しています(Elemeno Portfolio Demo)。自由に作ってみてください。

(原文:Off With Your Head! Build Better Web Apps with a Headless CMS

[翻訳:関 宏也/編集:Livit

Web Professionalトップへ

この記事の編集者は以下の記事をオススメしています