Contentful APIリクエストの実行 Nuxt.jsにブログコンテンツを表示しよう
  • 2022.01.08に公開
  • ブログ構築TS
  • 5. Nuxt.js×Contentfulセットアップ
  • No.2 / 3

今回達成すること

前回セットアップしたモジュールcontentfulを使って、Contentfulのコンテンツを取得し、Vueファイルに表示します。

2022-01-08 18-33-48

プラグイン contentful.tsをインポートする

ContentfulへAPIリクエストを投げるには、前回作成したプラグインファイルcontentful.tsimportします。

pages/index.vue<script>タグ下にimportを追加しましょう。

pages/index.vue
<script lang="ts">
import { Component, Vue } from 'nuxt-property-decorator'
// 追加
import client from '~/plugins/contentful'
</script>

asyncDataからAPI リクエストを行う

開発環境では、asyncData()を使用すればサーバーサイドでAPIリクエストを行うことができます。

Contentfulの複数のコンテンツを取得する場合は、getEntries()メソッドを使用します。

<script lang="ts">
@Component
export default class IndexPage extends Vue {  
  // 追加
  async asyncData (): Promise<{ entries: object }> {
    let entries: object = {}

    if (process.server) {
      await client.getEntries({
        content_type: 'blogPost',
        limit: 200,
        order: '-fields.publishDate'
      })
        .then((entry: object) => {
          entries = entry
        })
        .catch((error: object) =>
          console.log(error)
        )
    }

    return {
      entries
    }
  }
}
</script>
  • content_type ... 取得したいモデル名を指定する。

    ここの値は、Contentful上のモデル名ではなく、Content type idを指定する。

  • Content type id ... Contentful管理画面 > 「Content model」 > 「該当のモデルを選択」 > 右サイドバーの「CONTENT TYPE ID」より確認できる。

  • limit ... 取得するコンテンツ数を指定する。デフォルトは100。

    100以上のコンテンツを作成した場合、デフォルト値のままだと全てのコンテンツが取得できない。ここはよくハマるので覚えておく。

  • order ... API取得時のコンテンツの並び順を文字列で指定する。

    -をつければ降順となり、つけなければ昇順となる。

  • -fields.publishDate ... 公開日の新しい順にコンテンツを取得する。(降順)

コンテンツはブラウザをリロードした時のみに表示される

今回の設計はAPI Keyの漏洩を防ぐため、Keyをクライアントに登録していません。

開発環境のasyncData()メソッドは、クライアントでも実行されるため、Contentfulにリクエストが行われるとAPI Keyが不正なエラーを吐きます。

そのエラーを回避するためにprocess.serverを使用し、サーバーサイドでのみリクエストを行うように分岐処理をしています。

if (process.server) {
  // server only
}

コンテンツは

  • ホットリロード時やnuxt-linkのページ遷移(クライアント リクエスト)には表示されず、
  • ブラウザをリロードしたタイミング(サーバー リクエスト)のみ表示されます。

目的のコンテンツはitemsに格納されている

Contentfulから取得したentryオブジェクトには、itemsという配列を返すプロパティが存在します。

その配列内にcontent_typeで指定したモデルのコンテンツリストが格納されています。

contentful レスポンス
entry: {
  items: [/* Content model items */]
}

このitemsをHTMLに表示します。

pages/index.vue
<template>
      <!-- 追加 -->
      <h2>
        Contentful API test
      </h2>
      <template
        v-if="Object.keys(entries).length"
      >
        <b>
          Post
        </b>
        <ul>
          <li
            v-for="(post, i) in entries.items"
            :key="`post-${i}`"
          >
            id: {{ post.sys.id }}
            <ul>
              <li>
                title: {{ post.fields.title }}
              </li>
              <li>
                publishDate: {{ post.fields.publishDate }}
              </li>
              <li>
                updatedAt: {{ post.sys.updatedAt }}
              </li>
            </ul>
          </li>
        </ul>
      </template>
    </v-container>
  </div>
</template>

sysプロパティはContentfulが用意しているもの

sysプロパティには

  • コンテンツID
  • コンテンツタイプ
  • コンテンツ作成時間、更新時間

など、Contentfulが用意している値が格納されています。

fieldsプロパティは自分で用意した値が入っている

fieldsプロパティは、モデルに追加したフィールドの値が格納されています。

関連付けられたモデルのプロパティ

関連付けられたモデル(blogCategoryやblogTag)は、<blogPostフィールド名>.fields.<関連モデルのフィールド名>で取得します。

sysプロパティは全モデル共通のプロパティです。

pages/index.vue
<!-- 追加 -->
<li>
  categoryId: {{ post.fields.category.sys.id }}<br>
  categoryTitle: {{ post.fields.category.fields.title }}
</li>

1対多の関連付けモデルのプロパティ

tagsなどの1対多の関連付がある場合は配列が返されます。

pages/index.vue
<!-- 追加 -->
<ul
  v-if="post.fields.tags"
>
  <li
    v-for="(tag, tagI) in post.fields.tags"
    :key="`tag-${tagI}`"
  >
    tagId: {{ tag.sys.id }}<br>
    tagTitle: {{ tag.fields.title }}
  </li>
</ul>

画像やファイルなどのメディアの格納場所

メディア(画像やファイル)は、Contentfulが用意しているプロパティで取得します。

該当のデータは、<フィールド名>.fieldsに格納されています。

例えば、アイキャッチ画像はimageフィールドで登録しているので、取得するには以下のようになります。

  • image.fields.title ... メディアタイトル。
  • image.fields.file ... メディアデータ。imageファイルのURLはfields.file.urlに保存されている。
  • image.fields.file.details ... メディアの詳細データ。imageファイルのサイズなどが格納されている。
pages/index.vue
<!-- 追加 -->
<li>
  <img
    :src="`https://${post.fields.image.fields.file.url}`"
    :alt="post.fields.image.fields.title"
    :width="post.fields.image.fields.file.details.image.width / 10"
    :height="post.fields.image.fields.file.details.image.height / 10"
  >
</li>

コンテンツが表示されたindex.vue

最終的にindex.vueはこのような表示になりました。

公開日が新しい順にリストされていることも確認してください。

2022-01-08 18-33-48

関連モデルコンテンツはincludes.Entryに格納されている

blogPostに関連づくblogCategoryblogTagモデルのコンテンツは、includes.Entryの配列内に格納されています。

contentful レスポンス
entry: {
  includes: {
   Entry: [/* Reference model items */] 
  }
}

includes.Entryの使い所

includes.Entryには1次元配列で全ての関連モデルデータが格納されているので、自分でコンテンツを整理する必要があります。

APIリクエスト回数を減らせますが、同時にJavaScriptのコードが複雑になります。

  1. モデル単位でgetEntries()メソッドを実行するか
  2. includes.Entryを使用してAPIリクエストの回数を減らすか

これはコードの複雑さにより判断してください。

今回は、2つの関連モデルを整理するだけなので「2」のincludes.Entryを採用します。

includes.Entryの注意点

includes.Entryを使用する注意点として、blogPostモデルに関連付けていないコンテンツは取得できません。

作成だけして、関連付けしていないカテゴリーやタグは取得できないということです。

モデルタイプを取得するプロパティ

sysプロパティ内のcontentTypeにモデルIDが格納されています。

entry.sys.contentType.sys.id

ここの値が

  • blogCategoryの場合 => カテゴリー配列にpush
  • blogTagの場合 => タグ配列にpushすることで

1次元配列の関連モデルを整理します。

関連コンテンツを表示するindex.vue

index.vueで確認しましょう。

pages/index.vue
<template>
        <!-- 追加 -->
        <b>
          Post References
        </b>
        <ul>
          <li
            v-for="(entry, i) in entries.includes.Entry"
            :key="`entry-${i}`"
          >
            {{ entry.sys.contentType.sys.id }}<br>
            {{ entry.sys.id }}<br>
            {{ entry.fields.title }}
          </li>
        </ul>
      </template>
    </v-container>
  </div>
</template>

関連づけしたカテゴリーとタグが取得できました。

2022-01-08 19-03-30

今回の作業は以上です。

GitHubへのPushは次回に行います。

% git commit -am "Add contentful api reduest to index.vue"

次回は?

index.vueに表示したContentfulのコンテンツを、本番環境にデプロイします。

あなたの力になれること
私自身が独学でプログラミングを勉強してきたので、一人で学び続ける苦しみは痛いほど分かります。そこで、当時の私がこんなのあったら良いのにな、と思っていたサービスを立ち上げました。周りに質問できる人がいない、答えの調べ方が分からない、ここを聞きたいだけなのにスクールは高額すぎる。そんな方に向けた単発・短期間メンターサービスを行っています。
独学プログラマのサービス
ブログ構築TSの投稿
1
  • Nuxt.js×TypeScript開発環境構築
  • /
  • #01
Nuxt.jsをローカルPCに立ち上げよう
2
  • Nuxt.js×TypeScript開発環境構築
  • /
  • #02
Nuxt.jsプロジェクトをGitHubにPushしよう
3
  • Nuxt.js×TypeScript開発環境構築
  • /
  • #03
nuxt-property-decoratorのインストールとTypeScriptのセットアップ
1
  • Vuetifyセットアップ
  • /
  • #01
TypeScript環境のNuxt.jsにVuetifyを導入しよう
2
  • Vuetifyセットアップ
  • /
  • #02
VuetifyにカスタムCSSを追加してSASS変数を理解しよう
3
  • Vuetifyセットアップ
  • /
  • #03
VuetifyにカスタムSVGアイコンを追加しよう
1
  • NetlifyCLIを使ったNuxtデプロイ
  • /
  • #01
Netlify CLIをインストールして本番環境のサイトを作成しよう
2
  • NetlifyCLIを使ったNuxtデプロイ
  • /
  • #02
netlify.tomlを使ってNuxt.jsをNetlifyに手動デプロイしよう
1
  • Contentfulモデル構築
  • /
  • #01
Contentfulの料金とCommunityプランの無料枠を理解する
2
  • Contentfulモデル構築
  • /
  • #02
Contentfulへ新規会員登録、ロケールの変更、API Keyの発行を行う
3
  • Contentfulモデル構築
  • /
  • #03
Contentful ブログカテゴリーモデルを作成しよう
4
  • Contentfulモデル構築
  • /
  • #04
Contentful カテゴリーモデルに1対1で関連づくblogPostモデルを作成しよう
5
  • Contentfulモデル構築
  • /
  • #05
Contentful ブログ記事に1対多で関連づくplogTagモデルを作成しよう
6
  • Contentfulモデル構築
  • /
  • #06
Contentful カテゴリー・ブログ記事・タグコンテンツを作成しよう
1
  • Nuxt.js×Contentfulセットアップ
  • /
  • #01
Nuxt.js×Contentfulセットアップ。モジュールのインストールからAPI Keyの登録まで
2
  • Nuxt.js×Contentfulセットアップ
  • /
  • #02
Contentful APIリクエストの実行 Nuxt.jsにブログコンテンツを表示しよう
3
  • Nuxt.js×Contentfulセットアップ
  • /
  • #03
ContentfulAPIをNetlifyにデプロイしよう【Nuxt FullStaticのasyncDataとfetch】
1
  • Vuex×TypeScriptセットアップ
  • /
  • #01
Vuexの型付け vuex-module-decoratorsとnuxt-typed-vuexどちらを使用するか
2
  • Vuex×TypeScriptセットアップ
  • /
  • #02
nuxt-typed-vuexのインストールとセットアップ。Vuexの型定義と呼び出し方
3
  • Vuex×TypeScriptセットアップ
  • /
  • #03
VuexにContentfulの型定義ファイルとnuxtServerInitを追加しよう
4
  • Vuex×TypeScriptセットアップ
  • /
  • #04
VuexにContentfulAPIレスポンスを保存してVueファイルに表示しよう
1
  • コンテンツページ構築
  • /
  • #01
ブログアプリのページ設計とNuxt.jsの動的ルーティングについて理解しよう
2
  • コンテンツページ構築
  • /
  • #02
カテゴリーのコンテンツページを作成しよう【Nuxt.js×Contentful】
3
  • コンテンツページ構築
  • /
  • #03
カテゴリーに関連付くブログ記事一覧を表示しよう【Nuxt.js×Contentful】
4
  • コンテンツページ構築
  • /
  • #04
injectを使用して共通エラー処理メソッドを作成しよう【Nuxt×TypeScript】
5
  • コンテンツページ構築
  • /
  • #05
NuxtChildを使用してブログ記事ページを作成しよう【Nuxt.js×TypeScript】
6
  • コンテンツページ構築
  • /
  • #06
タグ一覧ページとタグ関連づく記事一覧を表示しよう【Nuxt.js×TypeScript】
7
  • コンテンツページ構築
  • /
  • #07
プライバシーポリシーページを作成しよう【Nuxt.js×TypeScript】
8
  • コンテンツページ構築
  • /
  • #08
@nuxtjs/i18nのインストールとセットアップ。ページタイトルの翻訳化【TypeScript】
1
  • NetlifyFunctionsを使った検索機能
  • /
  • #01
Netlify Functionsを使ってクエリを返す関数を作成しよう【Nuxt.js×TypeScript】
2
  • NetlifyFunctionsを使った検索機能
  • /
  • #02
Netlify Functionsプロジェクトをデプロイしよう【Nuxt.js×TypeScript】
3
  • NetlifyFunctionsを使った検索機能
  • /
  • #03
Nuxt.js × axiosセットアップ Netlify Functionsにリクエストを行う準備をしよう
4
  • NetlifyFunctionsを使った検索機能
  • /
  • #04
オリジン•CORS•プリフライトリクエストを理解する【Nuxt.js×Netlify Functions】
5
  • NetlifyFunctionsを使った検索機能
  • /
  • #05
Netlify Functionsを使ってフォームバリデーション機能を構築しよう【Nuxt.js】
6
  • NetlifyFunctionsを使った検索機能
  • /
  • #06
ツールバーに表示する検索フォームを作成しよう【Nuxt.js×TypeScript】
7
  • NetlifyFunctionsを使った検索機能
  • /
  • #07
検索ページを作成しよう【Vue propsとTypeScriptの書き方 解説】
8
  • NetlifyFunctionsを使った検索機能
  • /
  • #08
Netlify FunctionsからContentfulAPIリクエストを送ろう【Nuxt.js】
9
  • NetlifyFunctionsを使った検索機能
  • /
  • #09
検索ページに「もっと見る」ボタンを実装しよう【Nuxt.js×TypeScript】
1
  • ブログMarkdown対応
  • /
  • #01
@nuxtjs/markdownitのインストールとセットアップ【Nuxt.js×TypeScript】
2
  • ブログMarkdown対応
  • /
  • #02
Nuxt.js×markdown-it 外部リンクを別タブで開くプラグインを追加しよう
3
  • ブログMarkdown対応
  • /
  • #03
Nuxt.js×markdown-it 内部リンクをVueRouterで高速にページ遷移しよう
4
  • ブログMarkdown対応
  • /
  • #04
Nuxt.js×markdown-it アンカーリンクとブログ目次を自動生成しよう
独学プログラマ
独学でも、ここまでできるってよ。
CONTACT
Nuxt.js制作のご依頼は下記メールアドレスまでお送りください。