🗂

Next.js + microCMSでJamstackブログを作ろう(初心者編)

2023/02/14に公開

概要

今回は microCMS(HeadlessCMS) を使って、
Next.js + TypeScriptプロジェクトでJamstackブログをVercelにデプロイするまでをご紹介します。

事前準備

microCMS公式サイトで新規登録を済ませる。
※未登録の場合のみ。

microCMSとは

microCMSはAPIベースの日本製HeadlessCMSです。
主にブログのコンテンツ管理を担います。

環境

  • Windows 10
  • Vscode
  • Node.js 18.13.0
  • npm 8.19.3
  • Next.js 13.1.6
  • TypeScript 4.9.5
  • microcms-js-sdk 2.3.2
  • dayjs 1.11.7

1. Next.jsプロジェクトの準備

下記コマンドでNext.jsプロジェクトを作成します。

npx create-next-app@latest --typescript

詳しくは以下ページを参照ください。

https://zenn.dev/msk1206/articles/dba565eb3985b4

2. microCMSを準備する

microCMS公式にログインする。

2-1. サービス作成画面へ

次にサービス作成画面に進みます
既にサービス名とサービスIDがランダムで振り分けられていますのでそのまま使います。
それではサービス作成ボタンを押して下さい。
※以下画像ではセキュリティ上消して載せています。

microCMS-サービス作成ページ

2-2. APIの作成

次に画面の中央に複数項目が表示されていますので、迷わずブログをクリックします。
すると自動的にブログを作成に必要なAPIが作成されますので。
今回はそのまま利用します。
※リスト形式になります。
microCMS側の準備は一旦これで完了です。

3. microcms-js-sdkの準備

それではプロジェクト側でmicroCMSのブログを表示の準備をしていきましょう。
以下コマンドをターミナルに入力しmicroCMSを使う準備をします。

npm i --save microcms-js-sdk

3-1. 投稿日時を標準時に整える

後で記事の投稿日時を標準時で表示するために事前にdayjsをインストールして置きます。

npm i dayjs

おまけ

見た目とかを整えたい方はbootstrapとbootstrap-iconsも入れておきます。

npm i bootstrap @types/bootstrap bootstrap-icons

4. Envファイルを作成しよう

APIキーをフロントに公開しないようにenvファイルで保護します。
プロジェクトフォルダに以下の名前のファイルを新規作成します。

.env.development.local

.envファイルが作成出来たら以下の様に記述し保存します。

API_KEY=
SERVICE_DOMAIN=

4-1. APIキーとサービスドメインを.envファイルに追加

次にmicroCMSのコンテンツ一覧ページで右上の少し下のAPIプレビューをクリック!
スライドが開きますのでX-MICROCMS-API-KEY:の右側XXXXXXXXXの部分をコピーして
先ほど作成した.envファイルのAPI_KEY=の右側に貼り付けます。

X-MICROCMS-API-KEY:XXXXXXXXX

サービスドメインはサービス作成時のサービスIDと同様なのですが、こちらもコンテンツ一覧画面の左上のサービス名の下に記載があるのでコピーします。
ではサービスドメインをenvファイルのSERVICE_DOMAIN=の右側に張り付けてください。

5. client.tsを作成する

src/ directory 内に libs/ directory を作成します。
次に libs/ directory 内に以下の名前のファイルを作成します。

client.ts

ファイルが作成出来たら以下の様にコードを追加し保存します。

client.ts
import { createClient } from "microcms-js-sdk";

export const client = createClient({
  serviceDomain: process.env.SERVICE_DOMAIN || "",
  apiKey: process.env.API_KEY || "",
});

6. ブログ表示の準備

それではブログページを表示するまでの流れをご説明していきます。

6-1. 型を作っておく

src/types/ directory を作成する。
types/ の中に blog.ts を作成する。

blog.ts
export type Blog = {
  id: string;
  createdAt: string;
  updatedAt: string;
  publishedAt: string;
  revisedAt: string;
  title: string;
  body: string;
  eyecatch: {
    url: string;
    width: number;
    height: number;
  };
  category: [
    {
      name: string;
    },
    {
      name: string;
    },
    {
      name: string;
    },
    {
      name: string;
    },
    {
      name: string;
    }
  ];
};

6-2. posts.tsxを作成する

src/ の中の pages/ 内に posts.tsx というファイルを作成する。
以下のコードを追加する。

posts.tsx
import Link from "next/link";
import { client } from "@/libs/client";
import { Blog } from "@/types/blog";

type Props = {
  blog: Blog[];
};

const Posts: React.FC<Props> = ({ blog }) => {
  return (
    <div>
      <ul>
        {blog.map((blog) => (
          <li key={blog.id}>
            <Link href={`/blog/${blog.id}`}>{blog.title}</Link>
          </li>
        ))}
      </ul>
    </div>
  );
};

export default Posts;

export const getStaticProps = async () => {
  const data = await client.get({ endpoint: "blog" });

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

6-3. [id].tsxを作成する

それでは src/ の中にある pages/ 内に blog/ directory を作成しましょう。
作成した blog/ directory の中に以下の名前のファイルを作成する。

[ID].tsx

以下のコードを追加する。

[ID].tsx
import { client } from "@/libs/client";
import { Blog } from "@/types/blog";

type Props = {
  blog: Blog[];
};

const BlogId: React.FC<Props> = ({ blog }: any) => {
  return (
    <main>
      <h1>{blog.title}</h1>
      <p>{blog.publishedAt}</p>
      <div
        dangerouslySetInnerHTML={{
          __html: `${blog.content}`,
        }}
      />
    </main>
  );
};

export default BlogId;

export const getStaticPaths = async () => {
  const data = await client.get({ endpoint: "blog" });

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

export const getStaticProps = async (context: { params: { id: string } }) => {
  const id = context.params.id;
  const data = await client.get({ endpoint: "blog", contentId: id });

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

6-4. ブログサイトを表示してみる

では、localhost:3000での表示が可能になりましたので試しに表示しましょう。
ターミナルで以下コマンドを入力します。

npm run dev

7. dayjsで投稿時間を正規化する

お気づきの方もいらっしゃられると思いますので、このままだと投稿日時が判りづらいので
dayjsで正規化します。
正規化するには以下のコードを追加します。

import dayjs from "dayjs";
import utc from "dayjs/plugin/utc";
import timezone from "dayjs/plugin/timezone";
dayjs.extend(utc);
dayjs.extend(timezone);

完成形のコードが以下になります。

posts.tsx
import Link from "next/link";
import { client } from "@/libs/client";
import { Blog } from "@/types/blog";
import dayjs from "dayjs";
import utc from "dayjs/plugin/utc";
import timezone from "dayjs/plugin/timezone";
dayjs.extend(utc);
dayjs.extend(timezone);
import styles from "@/styles/Post.module.css";
import Image from "next/image";

type Props = {
  blog: Blog[];
};

const Posts: React.FC<Props> = ({ blog }) => {
  return (
    <div>
      <div className={styles.posts}>
        <ul className={styles.grid}>
          {blog.map((blog) => (
            <li key={blog.id}>
              <Link href={`/blog/${blog.id}`}>
                <Image
                  className={styles.eyecatch_img}
                  src={blog.eyecatch.url}
                  width={300}
                  height={175}
                  alt={blog.title}
                />
                <h5 className={styles.blogtitle}>{blog.title}</h5>
                <p className={styles.publishedAt}>
                  投稿日:
                  {dayjs
                    .utc(blog.publishedAt)
                    .tz("Asia/Tokyo")
                    .format(
                      "YYYY" +
                        "年" +
                        "MM" +
                        "月" +
                        "DD" +
                        "日" +
                        "hh" +
                        ":" +
                        "mm"
                    )}
                </p>
              </Link>
            </li>
          ))}
        </ul>
      </div>
    </div>
  );
};

export default Posts;

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

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

[id].tsx
import { client } from "@/libs/client";
import { Blog } from "@/types/blog";
import dayjs from "dayjs";
import utc from "dayjs/plugin/utc";
import timezone from "dayjs/plugin/timezone";
dayjs.extend(utc);
dayjs.extend(timezone);
import styles from "@/styles/Blogid.module.css";

type Props = {
  blog: Blog[];
};

const BlogId: React.FC<Props> = ({ blog }: any) => {
  return (
    <main>
      <div>
        <h1 className={styles.title}>{blog.title}</h1>
        <p className={styles.publishedAt}>
          投稿日:
          {dayjs
            .utc(blog.publishedAt)
            .tz("Asia/Tokyo")
            .format(
              "YYYY" + "年" + "MM" + "月" + "DD" + "日" + "hh" + ":" + "mm"
            )}
        </p>
        <p className={styles.category}>
          タグ:{blog.category && `${blog.category.name}`}
        </p>

        <div
          className={styles.post}
          dangerouslySetInnerHTML={{ __html: blog.content }}
        />
      </div>{" "}
    </main>
  );
};

export default BlogId;

export const getStaticPaths = async () => {
  const data = await client.get({ endpoint: "blog" });

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

export const getStaticProps = async (context: { params: { id: string } }) => {
  const id = context.params.id;
  const data = await client.get({ endpoint: "blog", contentId: id });

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

8. Vercelにデプロイする

今回は詳しい説明は省きます。

詳しくは以下ページを参照ください。

https://zenn.dev/msk1206/articles/dba565eb3985b4

8-1. Vercelに環境変数を設定する

Vercelの該当プロジェクトページを開き、画面の上にあるSettingタブをクリック。
次に画面左側のEnvironment Variablesをクリック。

Key と Value の入力する場所がありますのでそれぞれ以下の通り追加してください。

Key Value
API_KEY X-MICROCMS-API-KEY
SERVICE_DOMAIN サービスドメイン名

次にKey Valueの入力下にEnvironmentの項目があります。
Production と Development をクリックしチェックを付けます。

後はSaveを押して保存します。
これで本番環境でもブログページを表示する事が出来るようになります。

9. 完成したブログサイトを見てみよう

https://my-website-gamma-ten.vercel.app/

こちらがこの記事で作成したブログサイトです。
今回はデザイン等は一切考慮せずに作ったので興味のある方は次回も見て頂けると幸いです。

最後に

以上、Next.js +TypeScript プロジェクトでmicroCMSを使ったブログサイトを作ろうの記事でした。

ちなみにanyを使わずにコーディングする方法があれば良かったのですが、
筆者はTypescriptあんまり触ったことが無いので、変な書き方してたらすいません。
現段階では良い方法が解らなかったので、どなたかご教授下さる方がいれば幸いです。

追記

次回は以下2記事を追加予定です。

  • Next.js + TypeScript SEO編
  • Next.js + TypeScript + Bootstrap編

Discussion