Nuxt.js / Contentful でJAMstackなブログ開発編

福岡から世界中の"むずかしい"を簡単にする株式会社[diffeasy](https://diffeasy.com)CTOの西[@_takeshi_24](https://twitter.com/_takeshi_24)です。

この記事はアドベントカレンダー「[diffeasyCTO西の24(にし)日連続投稿チャレンジ Advent Calendar 2019](https://qiita.com/advent-calendar/2019/diffeasy24)」の22日目の記事です。

この記事は前回の「[JAMstackなブログ開設にチャレンジ](https://qiita.com/_takeshi_24/items/d8241d5327001d767067)」の続きです。
JAMstackなブログ開発にチャレンジしたい方、是非Qiitaアカウントか[twitterをフォロー](https://twitter.com/_takeshi_24)していただき、ツッコミやいいね！お願いします！

今回はContentfulで記事を投稿し、Nuxt.jsで表示するところまでを解説します。

## Contentful
### Contentfulのアカウント作成
まずはContentfulのアカウントを作成します。
Contentfulの[アカウント登録ページ](https://www.contentful.com/sign-up/)からアカウントを作成します。
後々GitHubと連携するので、GitHubアカウントで登録してください。

名前や組織名などを入力して登録を完了させてください。

### コンテンツを作成
アカウント登録が完了すると、コンテンツ作成に進みますが、画面で２つの選択肢が表示されます。
<img width="1362" alt="スクリーンショット 2019-12-22 18.54.30.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003/0ccdb899-0191-edb1-fc19-b700637c06bc.png">

これは最初にサンプルとして作られるスペースの選択のようです。
今回は左側の「I create content」を選択します。

ちなみに、左が通常のヘッドレスCMSで、右側をクリックすると、Gatsbyのスターターテンプレートが生成されます。
<img width="1178" alt="スクリーンショット 2019-12-22 20.50.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003/92e45df4-147b-8f3a-f9ea-51dfc2ef9703.png">

### スペースを作成
最初にサンプルスペースがすでに作成されていますが、今回は新規に作成します。
<img width="782" alt="スクリーンショット 2019-12-22 21.12.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003/862886f8-93dc-f860-3a22-d10ffb32a76c.png">
ここではフリープランを選択します。

<img width="782" alt="スクリーンショット 2019-12-22 21.12.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003/82841ee1-e2d4-50b9-f363-bba71965ac3f.png">
スペース名を入力して、「Create an empty space.」を選択します。

<img width="781" alt="スクリーンショット 2019-12-22 21.22.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003/4bad7226-20b0-8a74-e89c-8da2c11fa606.png">
内容を確認し、スペースを作成します。

### コンテンツタイプを登録
ブログの記事、カテゴリーなどのコンテンツタイプを登録します。
ヘッダーメニューの「Content model」をクリックします。

ここではブログの記事を投稿するための、「post」のコンテンツタイプを追加します。
<img width="701" alt="スクリーンショット 2019-12-22 22.38.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003/411f4b49-1b99-a68c-409f-6e10af1df03e.png">

「post」のコンテンツタイプにフィールドを追加していきます。
ブログ記事の場合、「タイトル」「本文」「公開日時」などが必要になります。
<img width="657" alt="スクリーンショット 2019-12-22 22.52.09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003/2ae2311a-8dda-f911-62d9-2cb3f98d7126.png">
<img width="756" alt="スクリーンショット 2019-12-22 22.51.59.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003/b8108386-f5bd-091e-d156-aceaed68f486.png">

フィールドを追加したら、画面右上の「save」ボタンで保存します。
<img width="1440" alt="スクリーンショット 2019-12-22 23.02.32.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003/2cf87c14-7a17-e6a4-b8af-3ec8ad18f34f.png">

###コンテンツを投稿<img width="1440" alt="スクリーンショット 2019-12-23 22.21.03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003/06f530a9-cbe9-3a44-4d2f-087c3fbad8e9.png">

先ほど登録した「post」コンテンツタイプのコンテンツを投稿します。
ヘッダーメニューの「Content」をクリックします。
「タイトル」「本文」「公開日時」を入力して、画面右の「Publish」をクリックして公開します。
<img width="1440" alt="スクリーンショット 2019-12-22 23.06.53.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003/e2498f02-1180-6176-432a-2faa9f22224d.png">

投稿したコンテンツが一覧表示されます。
<img width="1440" alt="スクリーンショット 2019-12-22 23.14.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003/544e07f6-3763-64c6-1685-1eceb8fd3cd5.png">

### APIキーの発行
スペースを選択した状態で、ヘッダーメニューのSettings→API keysを開き、画面左上の「Add API key」をクリックします。
<img width="1440" alt="スクリーンショット 2019-12-23 22.21.03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003/98af5caa-d549-6ce4-092a-8dc4a5da5757.png">
鍵が生成されて、「Space ID」と「Content Delivery API - access token」が発行されます。
こちらは後ほど使います。

## Nuxt.js
次に、Nuxt.jsでクライアント側のWebアプリケーションを作成します。

### Nuxt.jsのインストール
Nuxt.jsのインストールなどは「[Nuxt.jsとFirebaseとCloudFunctionsでWebアプリ開発 - 準備編](https://qiita.com/_takeshi_24/items/c26e4a664e27b652d9a6)」の記事の、Nuxt.jsの手順を参考にしてください。
※今回は静的サイトを生成するため、**SPAモードは選択しない**でください。
※こちらの記事では環境ごとに環境変数を切り替えるため、.envではなく、cross-envというパッケージを使います。

```terminal
$ yarn add cross-env
```

cross-env導入については、こちらの記事「[Nuxt.jsとFirebaseAuthentication/FirebaseUIでログイン認証](https://qiita.com/_takeshi_24/items/4ea05d5564a5d5ca9825)」を参考にしてください。


### Contentfulのパッケージを追加
Nuxt.jsからContentfulのコンテンツにアクセスするために、Contentfulのパッケージを追加します。
リッチテキストを表示するための「@contentful/rich-text-html-renderer」パッケージも追加します。

```terminal
$ yarn add --dev contentful @contentful/rich-text-html-renderer 
```

### Contentful接続の準備
Contentfulの接続情報をenvファイルに記載します。

```javascript:env.development.js
module.exports = {
  BASE_URL: 'http://localhost:3000',
  CTFL_SPACE: 'wxxxxxxxxxu',
  CTFL_ACCESS_TOKEN: '7LQxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxNM',
  CTFL_CONTENT_TYPE_POST: 'post'
}
```

ContentfulSDKをNuxt.jsで利用するために、pluginsにcontentful.jsを追加します。

```javascript:/plugins/contentful.js
const contentful = require('contentful')

module.exports = {
  createClient() {
    return contentful.createClient({
      space: process.env.CTFL_SPACE,
      accessToken: process.env.CTFL_ACCESS_TOKEN
    })
  }
}
```

### 記事一覧ページ
記事一覧を取得します。ContentfulSDKのAPIは以下のリンクを参考にしてください。
https://contentful.github.io/contentful.js/contentful/7.11.3/


```vuejs:/pages/posts/index.vue
<template>
  <section class="container">
    <ul>
      <li v-for="(post, index) in posts" :key="index">
        <nuxt-link :to="`/posts/${post.sys.id}`">{{
          post.fields.title
        }}</nuxt-link>
      </li>
    </ul>
  </section>
</template>
<script>
import { createClient } from '~/plugins/contentful.js'

const client = createClient()
export default {
  async asyncData({ params }) {
    // 記事一覧を取得
    const entries = await client.getEntries({
      content_type: process.env.CTFL_CONTENT_TYPE_POST
    })
    return {
      posts: entries.items
    }
  }
}
</script>

```

### 記事詳細ページ
記事詳細ページでは、投稿のリッチテキストをHTMLに変換して表示します。

```vuejs:/pages/posts/_id/index.vue
<template>
  <section class="container">
    <h1>{{ post.fields.title }}</h1>
    <v-row>
      <v-col>
        <div v-html="toHtmlString(post.fields.body)"></div>
      </v-col>
    </v-row>
  </section>
</template>
<script>
import { documentToHtmlString } from '@contentful/rich-text-html-renderer'
import { createClient } from '~/plugins/contentful.js'

const client = createClient()
export default {
  async asyncData({ params, payload }) {
    // IDをキーに記事を取得
    const entry = await client.getEntry(params.id)
    return {
      post: entry
    }
  },
  methods: {
    toHtmlString(obj) {
      return documentToHtmlString(obj)
    }
  }
}
</script>
```

以上で、Nuxt.jsを起動し、http://localhost:3000/posts/ にアクセスすると、記事一覧が表示され、そこのリンクから記事詳細ページが表示されます。

今回は、簡易的な表示ではありますが、Nuxt.jsでContentfulのAPIを利用して記事を表示するところまでできました。
次回はジェネレートした静的サイトを配信する手順について説明します！

「[Nuxt.js / Contentful / NetlifyでJAMstackなブログ開設](https://qiita.com/_takeshi_24/items/cf4d9b10c110830c1856)」につづく

## 参考サイト
[【入門】Nuxt.js + Contentful + Netlify で始める、JAMstack な CMS 構築](https://qiita.com/isihigameKoudai/items/3e45ade7c438176a4cc9)