Open11

Next.jsとmicroCMSでJamstackなブログを作る

donchandonchan

次の2本の記事(スクラップ)を参考に、Jamstackなブログを作っていく。

https://blog.microcms.io/microcms-next-jamstack-blog/
https://zenn.dev/hirokikameda/scraps/06f31571e32660

Jamstackの概要や利点は次の記事で学習
https://qiita.com/ozaki25/items/4075d03278d1fb51cc37

要件は次の通り

  • 無料で運用可能
  • 静的サイト側はReact/Nuxt.jsをTypeScriptで使う
  • マークダウンで書きたい
  • Google Analyticsを使いたい
  • このスクラップを書きながら調べるが、場合によっては方向転換も止む無し

今回は、「クルマのブログ」を立ち上げるという想定です。
ではいこう

donchandonchan

microCMSへのサービスの登録etc

アカウントを持っていなければ新規登録から。
https://app.microcms.io/signup

こんな感じで設定していきます。

ブログの一覧を返すAPIなので、リスト形式を選びます。

スキーマは基本的な設定で

コンテンツを適当に作成

APIプレビューでも確認(キーは伏せてます)

とてもわかりやすい!

donchandonchan

microCMSのコンテンツをNext.jsで取得する

microCMSのキーの管理

キーはGithubのプッシュ対象にしたくないので、.env.development.localに書いてあげます。

このファイルは、スキャフォールドした時点で、.gitignoreに追加されてます。
.env,.env.local
というかNuxtの機能です。
https://nextjs.org/docs/basic-features/environment-variables

これでprocess.{環境変数名}でキーなどのセキュアに扱う情報を取得できます。

microcms-js-sdkの準備

次のコマンドで、 microcms-js-sdk をインストール

$ npm i microcms-js-sdk

ルート階層に、libsフォルダ -> client.tsを作成してSDKの初期化を行います。
serviceDomainはXXXX.microcms.ioの場合、XXXXの部分になります。apiKeyは先ほど設定した環境変数を参照してください。

import { createClient } from "microcms-js-sdk";

export const client = createClient({
  serviceDomain: "car-blog",
  apiKey: process.env.API_KEY as string,
});

ブログの一覧を表示する

pages/index.ts を次のようにします。

getStaticProps()はビルド時に呼び出され、microCMSのコンテンツを取得してくれます。

import { MicroCMSListResponse } from "microcms-js-sdk";
import type { NextPage } from "next";

import Link from "next/link";
import { client } from "../../libs/client";
import { Blog } from "../types/EndPoints";

// データをテンプレートに受け渡す部分の処理を記述します
export const getStaticProps = async () => {
  const data = await client.get<MicroCMSListResponse<Blog>>({
    endpoint: "blog",
  });

  return {
    props: {
      blog: data.contents,
    },
  };
};

type Props = {
  blog: Blog[];
};
const Home: NextPage<Props> = ({ blog }: Props) => {
  return (
    <div>
      <ul>
        {blog.map((blog) => (
          <li key={blog.id}>
            <Link href={`/blog/${blog.id}`}>
              <a>{blog.title}</a>
            </Link>
          </li>
        ))}
      </ul>
    </div>
  );
};

export default Home;

こんな感じで表示されれば疎通はOKです!

記事ページを表示する

pages/[id].tsx ファイルはこんな感じに記述しました。
型は適宜付けてます。

import { client } from "../../../libs/client";
import { Blog, Context } from "../../types/EndPoints";
import React from "react";
import { MicroCMSListResponse } from "microcms-js-sdk";

type Props = {
  blog: Blog;
};
export default function BlogId({ blog }: Props) {
  return (
    <main>
      <h1>{blog.title}</h1>
      <p>{blog.publishedAt}</p>
      <div
        dangerouslySetInnerHTML={{
          __html: `${blog.body}`,
        }}
      />
    </main>
  );
}

// 静的生成のためのパスを指定します
export const getStaticPaths = async () => {
  const data: MicroCMSListResponse<Blog> = await client.get({
    endpoint: "blog",
  });

  const paths = data.contents.map((content) => `/blog/${content.id}`);
  return { paths, fallback: false };
};

// データをテンプレートに受け渡す部分の処理を記述します
export const getStaticProps = async (context: Context) => {
  const id = context.params.id;
  const data = await client.get<Blog>({ endpoint: "blog", contentId: id });

  return {
    props: {
      blog: data,
    },
  };
};

こんな感じで表示されれば、記事の一覧から、記事詳細ページまでの遷移はOKです!

donchandonchan

ビルドする

このStackはビルドありきなので、試しにローカル環境でビルドします。

次のコマンドを実行します。

$ npm run build

ただのNext.jsのエコシステムを利用したビルドです。

microCMSのコンテンツがしっかり静的ファイルとしてビルドされてますね!

あとはこの資材を適当なところにデプロイするだけです。

donchandonchan

microCMSのコンテンツを更新したらビルドして反映させる

このままの状態では、microCMS側で記事を書いて公開状態にしても、静的サイトなので、ビルド→デプロイしない限り反映されません。

microCMSの記事更新をhookして、CI/CDを回す必要があります。

Vercel側の設定

プロジェクトの設定から、Gitを選択します。その中のDeploy HooksからWebHooksを作成します。

※ 今回はmainブランチを対象にしてます

Create Hookを押下すると、WebHookURLが発行されます。

microCMS側の設定

上記で発行したWebHookURLを、microCMSに設定します。

API設定を開き、Webhookを選択し、カスタム通知を選択
こんな感じに設定。
通知設定はお好きに設定してください。

microCMS側で記事を投稿して、数十秒後にデプロイされていることを確認できるかと思います。

donchandonchan

カテゴリ機能を実装する

下記の通り、新たにAPIを追加する。

適当にカテゴリーを作りました。

ブログAPIの設定をする

前の手順で作成した「ブログ」APIスキーマを編集します。

これで、記事とカテゴリーの参照関係を作成できます。

カテゴリーを付けて記事を投稿するとき

下に表示されるカテゴリーから設定できます。

記事詳細画面にカテゴリを表示する。

// [id].tsx
export default function BlogId({ blog }: Props) {
  return (
    <main>
      <h1>{blog.title}</h1>
      <p>{blog.publishedAt}</p>
      <p className="category">{blog.category && `${blog.category.name}`}</p>
      <div
        dangerouslySetInnerHTML={{
          __html: `${blog.body}`,
        }}
      />
    </main>
  );
}

GOOD!

donchandonchan

〜一旦休憩〜

ここまででは
あまりにも質素なので、後日Styleを当てていきます。

donchandonchan

テンプレートを利用してブログっぽくしていく

ゴリゴリと実装してもいいのですが、面倒なので、OSSのテンプレートを使わせていただきます。

ちょうど、microCMS 様の自社ブログ(Nuxt製)をNextに移植されている方がいたので、こちらを参考にブログテンプレートとして移植しました。
https://zenn.dev/wattanx/articles/d45d5627ffef54