かなで技術日誌

プログラミングやエンジニアリング周りについて

主なアウトプットはScrapboxObsidianにまとめてます。

React+Contentful+Netlify+Google Domainで個人ブログを作る

久しぶりの投稿です。TypeScript+Reactの素振り用に前から気になっていたHeadless CMSのContentfulを使ってブログをNetlifyにホスティングしてみました。今ならCloudflare Pagesとかになるんでしょうか。

Netlifyから提供されるドメインではなく独自ドメインを設定する方法も合わせて追記しています。今回はGoogle Domainを使いました。お名前.comは使いたく無い

NextでもVueでも同じようにできると思うので置き換えてください。

参考になるか分かりませんが、記事の例で使用している自分の実装はこちらです。あくまで参考程度ですが、特にeslintとかprettierとかの設定は参考にしないでください。

話すこと

  • Contentfulでの設定方法
  • Contentfulのmodelの型定義
  • SDKの使い方
  • Netlifyでのデプロイ手順
  • GitHub Actionsの設定
  • Google Domainでのドメイン取得方法

話さないこと

  • TypeScript自体
  • Reactの環境構築
  • 全体の実装

お品書き

  1. Contentful設定
  2. SDKの使用方法
  3. modelの型定義
  4. Netlify設定&デプロイ
  5. ドメイン取得

Contentful設定

Contentful自体の日本語での説明はこちら(次世代Headless CMS「contentful」事始め)をみてください。

何はともあれログインしてspaceを作成します。基本Freeで大丈夫です。

f:id:kana_kanade:20210405224154p:plain

spaceを作成したらmodelを作成します。これが実際のデータ構造になります。

Contentfulは多くのfieldを用意しています。ReferenceはRDBの外部キーのような使い方ができます。今回はカテゴリとタグを紐づけるのに使用しました。 今回は記事のカテゴリとタグ、記事自体のmodelを用意します。 f:id:kana_kanade:20210406082733p:plain

まずカテゴリのmodelです。 f:id:kana_kanade:20210406082530p:plain 次にtagのmodelです。 f:id:kana_kanade:20210406082546p:plain 最後に記事のmodelにカテゴリとタグを紐付けます。 f:id:kana_kanade:20210406082551p:plain 記事本文は今回はMarkdownを指定してください。描画時にMarkdownをhtmlに変換します。 f:id:kana_kanade:20210406083042p:plain カテゴリとタグの紐付けはReferencesを選択するとOne ReferencesとMany Referencesが選択できます。カテゴリは記事に対して一対一、タグは一対多で一つの記事に複数紐づくようにしました。 f:id:kana_kanade:20210406083712p:plain

最終的に以下のようになります。 f:id:kana_kanade:20210406082524p:plain

最後に記事を取得するためのAPI keyを生成します。ヘッダーのSettings→API KeysからAdd API Keyを選択して生成します。 後でContentfulのSDKを使いますが、その際にSpace IDとContent Delivery API - access tokenが必要になります。 f:id:kana_kanade:20210406084536p:plain

これでContentfulは終わりです。

SDKの使用方法

Reactの一通りの環境構築が終わっている前提で、SDKのinstallと使い方の説明をします。

SDKをinstallします。

npm install --save contentful

記事一覧を取得する実装です。 createClientにSpace IDとaccess tokenを渡してContentfulClientApiインスタンスを生成します。 その後getEntriesで全体を取得できます。 ここでは実装していませんが、検索条件を付与することも可能です。詳しくはドキュメントを読んでください。

型定義については後で説明します。

import {ContentfulClientApi, createClient, Entry} from 'contentful';
import {Article as ContentfulArticle} from '../domain/contentful/article';

const getClient = (): ContentfulClientApi => {
  return createClient({
    space: process.env.REACT_APP_CONTENTFUL_SPACE_ID as string,
    accessToken: process.env.REACT_APP_CONTENTFUL_DELIVERY_API as string,
    resolveLinks: true,
  });
};
const getContent = async (
  contentType: string
): Promise<Entry<ContentfulArticle>[]> => {
  const client = getClient();
  const response = await client.getEntries<ContentfulArticle>({
    content_type: contentType,
  });
  return response.items;
};

modelの型定義

Contentfulの記事はここをanyにしてごまかしている実装がよく検索でヒットします。JavaScriptではなくTypeScriptを使っているのでanyに逃げずにちゃんと型定義をします。

先ほどのgetEntriesの戻り値の型はPromise<EntryCollection<T>>>です。 その戻り値のitemプロパティの型はEntry<T>でこれがmodelの実体になります。このTに自分で設定したmodelの型定義をあててあげれば型をつけて取得できます。

Entry型は以下のようなデータ構造になっています。

export interface Entry<T> {
    sys: Sys;
    fields: T;
    toPlainObject(): object;
    update(): Promise<Entry<T>>;
}

sysはuniqueなidや作成日時、更新日時などのメタデータが定義されています。 fieldsがモデルの定義になります。

よって今回の型定義は以下のようになります。

import {Sys} from 'contentful';
export type Category = {
  fields: {
    name: string;
  };
  sys: Sys;
};
export type Tag = {
  fields: {
    name: string;
    slug: string;
  };
  sys: Sys;
};
export type Article = {
  category: Category;
  content: string;
  tag: Tag[];
  title: string;
};

Article型はEntry型のfieldsプロパティの型を定義してsysプロパティの型定義は不要です。

Netlify設定&デプロイ

NetlifyはGitHub連携でpush時に自動でデプロイするのとSDKでのGitHub Actionsでのデプロイがあります。 starter planではビルドが月300分までなので、頻繁にデプロイしたいならGitHub Actionsでビルドしてデプロイするとお得です。 以下のような.github/workflows/deploy.yamlを用意してください。

masterブランチへのpush時に以下のworkflowが実行されます。

name: Deploy

on:
  push:
    branches:
      - master

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout source code
        uses: actions/checkout@v2

      - name: Cache node_modules
        uses: actions/cache@v1
        with:
          path: node_modules
          key: ${{ runner.OS }}-build-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.OS }}-build-
            ${{ runner.OS }}
      - name: Setup Node
        uses: actions/setup-node@v1
        with:
          node-version: 14.x

      - name: npm install and build
        run: |
          yarn install
          yarn build
      - name: Deploy to netlify
        run: npx netlify-cli deploy --dir=./build
        env:
          NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
          NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}

まずAUTH_TOKENとSITE_IDですが、アプリケーション設定画面に遷移するとNew access tokenとあるので押下するとAUTH_TOKENが取得できます。 f:id:kana_kanade:20210510234707p:plain

SITE_IDはhttps://app.netlify.com/sites/[自分のサイト名]/settings/generalに遷移するとAPI IDとあるのでこれがSITE_IDです。

f:id:kana_kanade:20210510234754j:plain

これらをGitHub Actionsで使うために環境変数として登録します。 GitHubリポジトリのSettingタブからSecretsを選択すると環境変数が登録できます。 f:id:kana_kanade:20210516182707p:plain

Google Domainsでカスタムドメイン設定

Google DomainsGoogleが提供するドメイン管理サービスです。 日本だとお名前.comがありますが、個人的には使いたくないので今回はGoogle Domainsを使います。

これ読んででいいですか?ダメ?

大切なのは、Google DomainsのカスタムリソースレコードにnetlifyのIPアドレスとwwwつきドメインを追加してください。 f:id:kana_kanade:20210531221952p:plain

netlifyのドメイン設定もここにあるんですが、

NetlifyのDomain ManagementからAdd Custom Domainで以下のような設定されていればおkです。 f:id:kana_kanade:20210531233456p:plain

最後にbaseURLを設定したカスタムドメインにすれば終わりです!

まとめ

覚えてしまえばそんなに難しく無いかなと思います

今度はNext.jsでcloudflare pagesにデプロイしてみたいです。