Next.jsとmicroCMSでJamstackなブログを作る
次の2本の記事(スクラップ)を参考に、Jamstackなブログを作っていく。
Jamstackの概要や利点は次の記事で学習
要件は次の通り
- 無料で運用可能
- 静的サイト側はReact/Nuxt.jsをTypeScriptで使う
- マークダウンで書きたい
- Google Analyticsを使いたい
- このスクラップを書きながら調べるが、場合によっては方向転換も止む無し
今回は、「クルマのブログ」を立ち上げるという想定です。
ではいこう
Nuxt.jsプロジェクト作成
--ts
オプションで初めからTSで開始できます。
$ npx create-next-app --ts car-blog
$ cd car-blog
$ npm run dev
ここまではドキュメント通りですね。
私はstyles
,public
,pages
はsrc
配下に置きました(serveし直してくださいね)
公式でもサポートされています。
microCMSへのサービスの登録etc
アカウントを持っていなければ新規登録から。
こんな感じで設定していきます。
ブログの一覧を返すAPIなので、リスト形式
を選びます。
スキーマは基本的な設定で
コンテンツを適当に作成
APIプレビューでも確認(キーは伏せてます)
とてもわかりやすい!
microCMSのコンテンツをNext.jsで取得する
microCMSのキーの管理
キーはGithubのプッシュ対象にしたくないので、.env.development.local
に書いてあげます。
このファイルは、スキャフォールドした時点で、.gitignore
に追加されてます。
※ .env
,.env.local
も
というかNuxtの機能です。
これで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です!
ビルドする
このStackはビルドありきなので、試しにローカル環境でビルドします。
次のコマンドを実行します。
$ npm run build
ただのNext.jsのエコシステムを利用したビルドです。
microCMSのコンテンツがしっかり静的ファイルとしてビルドされてますね!
あとはこの資材を適当なところにデプロイするだけです。
Vercelにデプロイ
JamstackにおいてCI/CDは必須
Firebase Hostingとかでもよかったんですけど、謹製のNext.jsと相性の良いとされるVercelで。
環境変数にmicroCMSのキーを登録するのをお忘れなく
スクショ撮り忘れたので、ググってくださいw
めっちゃ簡単です。
microCMSのコンテンツを更新したらビルドして反映させる
このままの状態では、microCMS側で記事を書いて公開状態にしても、静的サイトなので、ビルド→デプロイしない限り反映されません。
microCMSの記事更新をhookして、CI/CDを回す必要があります。
Vercel側の設定
プロジェクトの設定から、Git
を選択します。その中のDeploy Hooks
からWebHooksを作成します。
※ 今回はmainブランチを対象にしてます
Create Hook
を押下すると、WebHookURLが発行されます。
microCMS側の設定
上記で発行したWebHookURLを、microCMSに設定します。
API設定を開き、Webhook
を選択し、カスタム通知
を選択
こんな感じに設定。
通知設定はお好きに設定してください。
microCMS側で記事を投稿して、数十秒後にデプロイされていることを確認できるかと思います。
カテゴリ機能を実装する
下記の通り、新たに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!
〜一旦休憩〜
ここまででは
あまりにも質素なので、後日Styleを当てていきます。