#5 ブログを作った話 - 投稿した記事を表示する

当ブログ(sumii.io)を、Figma, Nuxt.js, Contentful, Netlifyを使って構築した方法を各パートにわけて紹介していきます。

今回はContentfulで投稿した記事をNuxt.jsで構築したブログに表示する方法を紹介します。

必要なパッケージのインストール

#3で実装したブログに必要なパッケージをインストールしていきます。
Contentfulのパッケージをインストールします。

yarn add contentful

今回はContentfulのAPIと接続するために必要なTokenやKeyなどをNuxt側で環境変数に持つことにします。

yarn add dotenv

API通信するための設定

Contentfulの管理画面に移動しヘッダーの「Settings」から「API keys」を選択します。
デフォルトで存在するKeyを使用するか、「Add API Key」を選択して新しくKeyを追加してください。
Keyの設定ページに飛ぶとSpace ID, Content Delivery API - access tokenがあると思います。こちらの2つをNuxt側の環境変数に設定していきます。

.envファイルの作成

Nuxt側のルートディレクリに.envという名前のファイルを作成し、環境変数としてContentfulのTokenやKeyを設定します。

CTF_SPACE_ID="Content Delivery API - access tokenを入れてください"
CTF_CDA_ACCESS_TOKEN="Space IDを入れてください"
CTF_BLOG_POST_TYPE_ID="ContentTypeIDを入れてください"

ContentTypeIDの確認方法は、Contentfulの管理画面でヘッダーの「Content model」を選択し、#4で作成したContentTypeをクリックすると設定ページの右側に表示されています。

プラグインファイルの作成

plugins/contentful.jsを作成して、以下のように記述します。

const contentful = require('contentful')

const config = {
  space: process.env.CTF_SPACE_ID,
  accessToken: process.env.CTF_CDA_ACCESS_TOKEN
}

module.exports = {
  createClient () {
    return contentful.createClient(config)
  }
}

nuxt.config.jsに設定を追記する

nuxt.config.jsに設定を追記します。

require('dotenv').config()

export default {
  …
  env: {
    CTF_SPACE_ID: process.env.CTF_SPACE_ID,
    CTF_CDA_ACCESS_TOKEN: process.env.CTF_CDA_ACCESS_TOKEN,
    CTF_BLOG_POST_TYPE_ID: process.env.CTF_BLOG_POST_TYPE_ID
  }
}

以上でContentfulの記事をAPIを使って取得する準備が整いました。

記事を取得する

では実際にContentfulから記事を取得して一覧を表示してみましょう。
#3で作成した記事一覧ページを修正していきます。

#3では以下のように、とりあえずの表示用としてJsonファイルから取得していました。

<script>
…
import posts from "~/assets/posts.json"

export default {
  …
  data() {
    return {
      posts: posts
    }
  }
}
</script>

<template>
  …
  <card-list :posts="posts"/>
</template>

data() {…}の部分をasyncData() {…}に置き換え、API通信処理を書いていきます。
asyncDataメソッドについてNuxtのドキュメントを御覧ください。

/** pages/index.vue */

<script>
import { createClient } from "~/plugins/contentful.js"

const client = createClient()

export default {
  ……
  async asyncData({ env, params }) {
    return await clinet
      .getEntries({
        content_type: env.CTF_BLOG_POST_TYPE_ID,
        order: "-fields.date"
      })
      .then(resp => ({
        posts: resp.items
      }))
      .catch(console.error)
  }
}
</script>

<template>
  …
  <card-list :posts="posts"/>
</template>

これでContentfulで投稿した記事を取得して表示することができますが、Contentfulから取得した記事の一覧(resp.items)をコンソールで確認すると、fieldsというものの中に投稿した記事のタイトルやカテゴリーの情報などが入っています。
なのでCardListを以下のように修正します。

/** components/CardList.vue */
……
<template>
  <div class="card-list">
    <card
      class="card"
      v-for="post in posts"
      :title="post.fields.title"
      :category="post.fields.category"
      :tags="post.fields.tags"
      :date="post.fields.date"
      :slug="post.fields.slug"
      :key="post.fields.id"
    />
  </div>
</template>

これで問題なく記事一覧が表示されるかと思います。

記事詳細ページを表示する

次に記事詳細ページを修正していきます。
こちらも記事一覧と同じく、表示用のJsonファイルから取得していた部分に変更を加えます。

/** pages/post/_slug.vue */

<script>
……
async asyncData({ env, params }) {
  return await client
   .getEntries({
     content_type: env.CTF_BLOG_POST_TYPE_ID,
     "fields.slug": params.slug,
     order: "-sys.createdAt"
   })
   .then(resp => ({
     post: resp.items[0].fields
   }))
   .catch(console.error)
},
……
</script>

<template>
  <container>
    <article>
      <!-- header component -->
      <post-content :content="post.content"/>
      <!-- footer component -->
    </article>
  </container>
</template>

これで投稿した内容が記事詳細ページに反映されたかと思います。

最後に

API通信処理部分の詳しい説明は省きましたが、別の記事で紹介しようと思います。
次はNetlifyを使ったホスティングのやり方と公開手順までを紹介します。