Next.jsにmicroCMS連携してブログサイト作ってみた

## この記事の目的
- Next.jsでブログを作成したときの備忘録
- Next.jsで指定したID情報をAPI取得する


## 概要
Next.jsでブログサイトの側だけ作成したので、APIでブログのデータを取得し表示する部分についてまとめています。
APIについてはmicroCMSを使用しています。
[実際のブログ記事はこちら](https://tech-blog-iota-gray.vercel.app/)
![スクリーンショット 2023-04-02 21.56.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/462658/420df127-976d-8aef-13ee-85e7b963d34b.png)

## 全体の流れ
- ①microCMS登録
- ②microCMSの準備（ブログ基本情報、ブログ記事作成）
- ③Next.jsでAPI設定
- ④Next.jsでライブラリのインストール
- ⑤Next.jsでAPI取得処理の実装


##  ①microCMS登録
### microCMSとは
>APIベースの日本製の中でも最も代表的なヘッドレスCMSです。国産のヘッドレスCMSとして最初に誕生し、国内では最も多くの利用者を誇ります。マニュアルが丁寧にまとめられており利用しやすいことでも人気を集めております。

簡単にいうとmicroCMSを使えば、ものすごく簡単にAPIの作成が行えるってことですかね

microCMS登録については[microCMSのはじめ方](https://blog.microcms.io/getting-started/)を参考にして下さい。
アカウント登録はすぐ終わります

##  ②microCMSの準備（ブログ基本情報、ブログ記事作成）
### サービス作成
![スクリーンショット 2023-04-02 22.20.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/462658/685c973d-3afe-ca8b-3eda-a3c4d9719c59.png)

### API作成
サービス作成後「コンテンツ（API）」部分の「＋」をクリックしAPIを作成していきます
![スクリーンショット 2023-04-02 22.22.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/462658/faad9198-6d85-9298-2d80-8de932b32feb.png)
API名とエンドポイント名を決めていきます。
![スクリーンショット 2023-04-02 22.23.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/462658/48abcfd1-b9a3-7ca8-d734-ad2a5cdce798.png)

### APIのフィールド設定（APIスキーマ）
![スクリーンショット 2023-04-02 22.28.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/462658/5608cb8e-9bfb-1388-e201-d3d6643bcf56.png)
本記事で紹介しているブログのフィールド設定は下記のようになっています
|    フィールドID   |   表示名      |   種類      |
|:----------:|:---------------:|:---------------:|
| title  |　タイトル |　テキストフィールド |
| description  |　サブタイトル |　テキストフィールド |
| content  |　内容 |　リッチエディタ |
| img  |　アイキャッチ |　画像 |
| category  |　カテゴリ |　セレクトフィールド |
| type  |　ブログ種別 |　セレクトフィールド |
| authorName  |　著者名 |　セレクトフィールド |
| authorImg  |　著者画像 |　画像 |
| authorDirector  |　著者役職 |　セレクトフィールド |

### ブログ記事作成
APIのフィールド設定が完了したらテスト用にブログを作成していきます。
APIフィールドは後から追加もできるので、慣れてきてから設定を増やすのもありかと思います
![スクリーンショット 2023-04-02 22.29.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/462658/e684bf9a-d50c-3fee-0bbc-65efe5b6c628.png)

## ③Next.jsでAPI設定
ここまででAPIとブログの作成が完了したので、Next.js側で呼び出せるように設定していきます。
```.env
API_KEY=APIキーを貼り付ける
MICROCMS_ENDPOINT=blogs
MICROCMS_DOMAIN=pikimarublog
```
API_KEYは以下の画像の「APIキー」をクリック後に出てくるAPIキーを貼り付けて下さい
![スクリーンショット 2023-04-02 22.59.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/462658/e78db9f8-4d11-798a-ce0b-499344dfae3b.png)
MICROCMS_ENDPOINTは以下の画像のエンドポイントに記載あるものを記載する
MICROCMS_DOMAINは「microcms.io」の前の部分を記載する
![スクリーンショット 2023-04-02 23.00.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/462658/f8f236a0-a930-906d-4a07-6af81ec94370.png)


## ④Next.jsでライブラリのインストールと
### microcms-js-sdkをインストール
APIリクエストには公式が提供しているmicrocms-js-sdkを使います。
[microCMSのJavaScript SDK公式サイト](https://www.npmjs.com/package/microcms-js-sdk)
```zsh
npm install microcms-js-sdk
```

## ⑤Next.jsでAPI取得処理の実装
### microCMSと接続
microCMSと接続を行う部分の記載をしていきます。
今回はlibディレクトリに`client.tsx`というファイルを作成しています
`serviceDomain`と`apiKey`の値は 先ほど設定した`env`ファイルを参照します。
```lib/client.tsx
import { createClient } from "microcms-js-sdk";

// microCMSと接続 
export const client = createClient({
    serviceDomain: process.env.MICROCMS_DOMAIN || '',
    apiKey: process.env.API_KEY || '',
})
```
### APIからデータを取得
ここでは一度最新のブログ記事を１００件取得しそのから直近の１５件を画面表示するように取得しています
```pages/index.tsx
import { Format } from '../layout/format'
import { Latest } from '../components/latest'

import type { Blog } from '../types/blog';
import { client } from "../lib/client";

type Props = {
  latestBlogs: Array<Blog>;
};

export default function Home({ latestBlogs }: Props) {
  return (
    <Format>
      <Latest blogs={latestBlogs} />
    </Format>
  )
}

export const getStaticProps = async () => {
  // APIからブログデータを全て取得
  const allData = await client.get({
    endpoint: process.env.MICROCMS_ENDPOINT || '',
    queries: {
      orders: '-createdAt', limit: 100 // 100件取得
    }
  });
  // const allBlogs = allData.contents;
  const allBlogs: Blog[] = allData.contents;

  // 最新のブログ記事を15件取得し「latestBlogs」に格納
  const latestBlogs = allBlogs.slice(0, 15);

  return {
    props: {
      latestBlogs,
    },
  }
};

```

<details><summary>カテゴリごとやブログの種別ごとに取得している`pages/index.tsx`コードをクリックして全て表示</summary><div>

```pages/index.tsx
import { Format } from '../layout/format'
import { Trending } from '../components/trending'
import { Latest } from '../components/latest'
import { Popular } from '../components/popular'
import { Category } from '../components/category'

import type { Blog } from '../types/blog';
import { client } from "../lib/client";

type Props = {
  latestBlogs: Array<Blog>;
  trendBlogs: Array<Blog>;
  popularBlogs: Array<Blog>;
  categoryBlogs: Record<string, Array<Blog>>; // categoryごとに分けた配列を持つオブジェクト
};

export default function Home({ trendBlogs, latestBlogs, popularBlogs, categoryBlogs }: Props) {
  return (
    <Format>
      <Trending blogs={trendBlogs} />
      <Latest blogs={latestBlogs} />
      <Popular blogs={popularBlogs} />
      <Category blogs={categoryBlogs as Record<string, Blog[]>} />
    </Format>
  )
}

export const getStaticProps = async () => {
  // APIからブログデータを全て取得
  const allData = await client.get({
    endpoint: 'blogs',
    queries: {
      orders: '-createdAt', limit: 100 // 100件取得
    }
  });
  // const allBlogs = allData.contents;
  const allBlogs: Blog[] = allData.contents;

  // 最新のブログ記事を15件取得し「latestBlogs」に格納
  const latestBlogs = allBlogs.slice(0, 15);

  // ブログ種別（type）がtrenddeあるデータを取得し「trendBlogs」に格納する
  const trendData = allBlogs.filter((blog) => blog.type.includes('trend'));
  const trendBlogs = trendData.slice(0, 10);

  // ブログ種別（type）がpopularのものから15件取得し「popularBlogs」に格納
  const popularData: Array<Blog> = allBlogs.filter(blog => blog.type.includes('popular'));
  // 最新順に15件取得し「popularBlogs」に格納
  const popularBlogs: Array<Blog> = popularData.slice(0, 10);

  // カテゴリーごとにデータを整理し「categoryBlogs」に格納
  const categoryBlogs: Record<string, Array<Blog>> = {}; // categoryごとに分けたオブジェクトを用意
  allBlogs.forEach((blog) => {
    if (categoryBlogs[blog.category]) {
      // すでにカテゴリーの配列があれば追加
      categoryBlogs[blog.category].push(blog);
    } else {
      // カテゴリーの配列がなければ新しく作成
      categoryBlogs[blog.category] = [blog];
    }
  });

  return {
    props: {
      latestBlogs,
      trendBlogs,
      popularBlogs,
      categoryBlogs,
    },
  }
};

```
</div></details>

## 補足
「process.env.API_KEYがundefined」のエラーといったようなエラーが出た場合は以下の２点を試してみて下さい
- `npm run dev`を再度実行
- `next.config.js`に環境変数の設定する

next.config.js に環境変数の設定が含まれていないとエラーが発生する可能性があります
nextConfig オブジェクトに env を追加して、環境変数を設定しましょう。
```next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  images: {
    domains: ['images.microcms-assets.io'],
  },
  env: {
    API_KEY: process.env.API_KEY,
  },
};

module.exports = nextConfig;

```

## まとめ
今回はmicroCMSを使用してブログ記事をAPI取得できるようにしてみました。
microCMSとVercelを連携することでmicroCMSでブログを作成や変更した際に自動的にビルドされてブログが更新されるのでかなり便利だと感じました。
ブログ自体はスタイルやその他コンテンツがまだ追加できていない箇所が多いのでこれからどんどん追加していきたいな、、