astamuse Lab

astamuse Labとは、アスタミューゼのエンジニアとデザイナーのブログです。アスタミューゼの事業・サービスを支えている知識と舞台裏の今を発信しています。

strapi+nuxt.jsでHeadless CMSの夜明けを感じる

デザイン部でフロントエンドエンジニアをしているkitoです。
近年のCMS界隈には、いわゆるHeadless化の波がきています。CMSのHeadless化とは、CMS(Content Management System)からクライアントサイドを切り離し、クライアントサイドのフレームワークでUIを構築するアーキテクチャです。

デカップルド・アーキテクチャと呼ばれることがあるようですが、サーバサイドとクライアントサイドがAPIを通じてコンテンツをやりとりすることで、Webアプリだけではなくてネイティブアプリからの要求にも適切に答えられるようになります。スケーリングに関しても、Headless CMSの方が取り回しが良いのではないでしょうか。

WordpressがWP REST APIを開発し、Headless化へと道筋をつけたことは特筆すべきです。WP REST APIの公式サイトでは、冒頭に「WordPress はアプリケーションフレームワークへと生まれ変わろうとしています。」と気負って書かれています。
とはいえ、Headless CMSの主役が、WordPressになるかどうかは未知数です。WordPressのエコシステムは巨大で、蓄積されたリソースは非常に豊かであると思いますが、クライアントサイドのフレームワークとの相性は、おそらく現時点ではReact.jsやVue.jsの周辺から派生してきたHeadless CMSの方が優っているのではないかと思います。

今回は、まだalpha版ですが使ってみて好感触だったstrapiというHeadless CMSと、クライアントサイドはVue.jsのフレームワークでSSR(サーバーサイドレンダリング)に対応しているNuxt.jsをあわせて使ってみたいと思います。

strapi

まずHeadless CMSのstrapiをインストールします。(Node.js 10.x以上、NPM 6.x以上が必要になります)

npm install strapi@alpha -g

インストールできているか確認します。

strapi -v

インストールされていれば、「3.0.0-alpha.x」のようなバージョン番号が表示されます。

データベースを任意に作成してください。MongoDBやPostgreSQLも使えますが、今回はMySQLを使います。

mysql -u root -p
mysql> create database strapi_db;

下記コマンドでアプリを作成していきます。 my-project以下にstrapiとnuxt.jsをそれぞれインストールします。

mkdir my-project
cd my-project
strapi new strapi

使用するデータベースを聞かれるので、MySQLを選択します。

Lets configurate the connection to your database:
  Choose your main database:
  MongoDB
  Postgres
❯ MySQL

データベースの名前を入力してください。

Database name: strapi_db 

続いて、Hostやport、UsernameとPasswordを聞かれるのでそれぞれ入力してください。
これでmy-project以下にstrapiがインストールされました。下記でstrapiを起動してみましょう。

cd strapi
strapi start

ブラウザが自動で起動するのでユーザ名、パスワード、メールアドレスを入力し「Ready to start」をクリックしてください。
f:id:astamuse:20190116110820p:plain 下記のようなadmin画面にログインできます。これでstrapiのインストールは完了です。続いてデータを入力します。 f:id:astamuse:20190116110812p:plain

コンテンツタイプ作成から「コンテンツタイプを追加」をクリックします。今回は名前をpostにします。次に「新しいフィールドを追加」を選択し、Stringをクリックして名前を「title」と入力します。続いて、textを選択して「content」、メディアを選択して「cover」と入力します。(「保存」のクリックを忘れないようにしましょう) f:id:astamuse:20190116110800p:plain f:id:astamuse:20190116110753p:plain

次にロールと権限から、Public > Postの権限を変更します。今回はスクリーンショットのようにすべてOKにしてあります。 f:id:astamuse:20190116110750p:plain サイドカラムにあるCONTENT TYPESの箇所にPostがあると思います。そこから、実際のデータを適当に入力してください。
http://localhost:1337/postsにアクセスすると、下記のような先ほど入力したデータがjson形式で表示されるでしょう。これでstrapiの設定は完了です。次にNuxt.jsのインストールです。

[
  {
  "id": 1,
  "title": "タイトル1",
  "content": "テキスト1テキスト1",
  "created_at": "2019-01-15T05:14:40.000Z",
  "updated_at": "2019-01-15T05:14:40.000Z",
  "cover": {
  "id": 1,
  "name": "150x150.png",
  "hash": "cffd30f367994f9788ca31c4ab0018af",
  "sha256": "Lny0dZHead23KZOVGlnYl7e0FTu-FQqzmeokqqZJZxE",
  "ext": ".png",
  "mime": "image/png",
  "size": "0.86",
  "url": "/uploads/cffd30f367994f9788ca31c4ab0018af.png",
  "provider": "local",
  "public_id": null,
  "created_at": "2019-01-15T05:14:40.000Z",
  "updated_at": "2019-01-15T05:14:40.000Z"
  }
  },
  {
  "id": 2,
  "title": "タイトル2",
  "content": "テキスト2テキスト2",
  "created_at": "2019-01-15T05:15:14.000Z",
  "updated_at": "2019-01-15T05:15:14.000Z",
  "cover": {
  "id": 2,
  "name": "150x150.png",
  "hash": "4646c71aaeee40f687cd263cf6afd9f2",
  "sha256": "Lny0dZHead23KZOVGlnYl7e0FTu-FQqzmeokqqZJZxE",
  "ext": ".png",
  "mime": "image/png",
  "size": "0.86",
  "url": "/uploads/4646c71aaeee40f687cd263cf6afd9f2.png",
  "provider": "local",
  "public_id": null,
  "created_at": "2019-01-15T05:15:14.000Z",
  "updated_at": "2019-01-15T05:15:14.000Z"
  }
  },
  {
  "id": 3,
  "title": "タイトル3",
  "content": "テキスト3テキスト3",
  "created_at": "2019-01-15T05:15:31.000Z",
  "updated_at": "2019-01-15T05:15:31.000Z",
  "cover": {
  "id": 3,
  "name": "150x150.png",
  "hash": "436a2f656ea843f8bb1eda6f2b404794",
  "sha256": "Lny0dZHead23KZOVGlnYl7e0FTu-FQqzmeokqqZJZxE",
  "ext": ".png",
  "mime": "image/png",
  "size": "0.86",
  "url": "/uploads/436a2f656ea843f8bb1eda6f2b404794.png",
  "provider": "local",
  "public_id": null,
  "created_at": "2019-01-15T05:15:31.000Z",
  "updated_at": "2019-01-15T05:15:31.000Z"
  }
  }
]

Nuxt.js

先ほど設定したstrapiのjsonデータを、Nuxt.jsで取得して表示させます。 まずmy-projectにNuxt.jsをインストールします。yarnもしくはnpxが必要です。 my-project直下で下記コマンドを実行してください。Use axios moduleはyesにして、それ以外はdefaultでOKです。

yarn create nuxt-app nuxtjs
cd nuxtjs
npm run dev

http://localhost:3000/ にブラウザでアクセスすると下記のように表示されると思います。これでNuxt.jsのインストールは完了です。 f:id:astamuse:20190116110746p:plain

次に、/my-project/nuxtjs/pages にposts.vueファイルを作成して、下記コードを記述してください。 Nuxt.jsは、pages以下のvueファイルとurlが連動するようにルーティングされる規約なので、http://localhost:3000/posts のファイルを作成していることになります。

<template>
  <section class="container">
     <ul>
       <li v-for="post in posts" :key="post.id">{{post.id}}:{{post.title}}{{post.content}}、<img :src="`http://localhost:1337/${post.cover.url}`"></li>
     </ul>
  </section>
</template>

<script>
import axios from 'axios'

export default{

  async asyncData({app}){
    let res = await axios.get('http://localhost:1337/posts')
    return {posts : res.data}
  }

}
</script>

asyncDataは、Nuxt.jsのメソッドでページコンポーネントがロードされる前に呼び出され、ここでgetしたpostsデータはSSR(サーバサイドレンダリング)されます。strapiとnuxt.jsを起動させた状態で、 http://localhost:3000/postsにアクセスすると、先ほど入力したデータが表示されていると思います。(ブラウザでソースを見て頂ければわかりますが、SSRされています)
これだと流石に見た目がアレなので、Bootstrapで少しスタイルを修正します。
/my-project/nuxtjs/nuxt.config.js のlinkにbootstrapのcssを追加します。

  head: {
    title: pkg.name,
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: pkg.description }
    ],
    link: [
      { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
      { rel: 'stylesheet', href: 'https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css' }
    ]
  },

/my-project/nuxtjs/layouts/default.vueのtemplateにnavを追加

<template>
    <div>
        <nav class="navbar navbar-light bg-dark">
            <a class="navbar-brand text-light" href="#">
                strapi + Nuxt.js
            </a>
        </nav>
        <nuxt/>
    </div>
</template>

/my-project/nuxtjs/pages/posts.vueを下記のように変更します。

<template>
    <div class="container">
        <div class="starter-template">
            <div class="card-deck">
                <div class="card" v-for="post in posts" :key="post.id">
                    <img class="card-img-top" :src="`http://localhost:1337/${post.cover.url}`">
                    <div class="card-body">
                        <h5 class="card-title">{{post.title}}</h5>
                        <p class="card-text">{{post.content}}</p>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
    import axios from 'axios'

    export default {
        async asyncData({app}) {
            let res = await axios.get('http://localhost:1337/posts')
            return {posts: res.data}
        }
    }
</script>

<style>
    .starter-template {
        padding: 3rem 1.5rem;
        text-align: center;
    }
</style>

strapiはcmsなので、もちろん画像やテキストを先ほどのadmin画面から変更できます。

f:id:astamuse:20190116110739p:plain 手順としては多少長いですが、思いのほか簡単にインストールできたのではないでしょうか。
個人的には、strapiとNuxt.jsで旧来のモノリシックなCMSでは得られない風通しの良さを感じました。CMSとクライアントサイドが疎結合になることで、必要であればCMS側だけを変更したり、クライアントサイドのフレームワークを変更できるようになります。また、Nuxt.jsのようなクライアントサイドのフレームワークを導入することでGoogle的に質の高いサイトを低コストで構築可能で、メリットは大きいでしょう。

アスタミューゼでは、エンジニア・デザイナーを募集中です。ご興味のある方は遠慮なく採用サイトからご応募ください。お待ちしています。

Copyright © astamuse company, ltd. all rights reserved.