ブログサイトをmicroCMSで開発するときに最低限必要なAPI集

[ACCESS Advent Calender 2021](https://qiita.com/advent-calendar/2021/access) 17日目になります。

今年APIベースの日本製ヘッドレスCMSの「microCMS」を使用して[ブログサイト](https://cotoblog.jp/)を開発したのでそのときに設定したAPIについて書きます。

## 開発環境

 - View: Next.jsで静的サイト生成(SSG) v11.1.1
 - Hosting: Vercel
 - HTTP Client Library: axios v0.24.0

### ブログ構成

 - カテゴリーにはサブカテゴリーが存在する。例: カテゴリー：音楽、サブカテゴリー：ジャズ
 - カテゴリーの投稿一覧ページ
 - サブカテゴリーの投稿一覧ページ
 - 詳細記事
 - 詳細記事には「次の記事」「前の記事」ボタンがあり、押下するとその記事に遷移
 - 詳細記事にはその記事のサブカテゴリーの関連記事を表示


## ブログサイトで最低限必要なAPI作成

### instance

 - 各APIのBaseとなるinstance。このinstanceを各APIで呼ぶ

```javascript
import axiosBase from 'axios';

const instance = axiosBase.create({
  baseURL: {resource名を除外したエンドポイント 例: https://hoge.microcms.io/api/v1},
  headers: {
    'Content-Type': 'application/json',
    'X-API-KEY': {環境変数で管理するmicroCMSで発行されるAPIキー},
  },
  responseType: 'json',
});

export default instance;
```

### カテゴリーの投稿一覧

 - microCMSでは1度のリクエストで取得できるdata数は10がMax
 - `offset` はページネーション開発時に必要なクエリ
 - `limit` はデフォルトでは `10`。TOPページでは表示数を制限したので設定
 
```javascript:API
export const getAllHogePosts = ({
  offset = 0,
  limit = PER_PAGE,
}) =>
  instance.get('/hoge', {
    params: {
      offset,
      limit,
    },
  });
```

### ページネーション作成時のAPI

 - URLは `hoge.jp/{カテゴリー名}/{page数}` とする。例: hoge.jp/foo/2

``` javascript:getStaticProps
const { id } = context.params as Params;
const response = await getAllHogePosts({ offset: typeof id === 'string' ? (parseInt(id, 10) - 1) * PER_PAGE : 0,});
```

### サブカテゴリーの投稿一覧

 - クエリ `filters` で サブカテゴリーIDに一致したカテゴリー記事のみを取得

```javascript:API
export const getAllHogeSubCategoryPosts = ({
  subCategoryId,
}) =>
  instance.get('/hoge', {
    params: {
      filters: `subCategory[equals]${subCategoryId}`,
    },
  });
```

```javascript:getStaticProps
const params = context.params as Params;
const response = await getAllHogeSubCategoryPosts({ subCategoryId: params.id });
```

### 詳細記事

```javascript:API
export const getHogePosts = ({ id }) => instance.get(`/hoge/${id}`);
```

```javascript:getStaticProps
const params = context.params as Params;
const response = await getHogePosts({ id: params.id });
```

### 詳細記事に「次の記事」「前の記事」のLink設定

 - `targetFilter` は詳細記事の公開日時を指定。クエリ`filters`でその公開日時から新しい/古い記事を1件取得
 - 取得対象は記事のidのみ。そのidを元に遷移先のURLを生成
 - クエリ`orders`で記事一覧を降順/昇順に並び替え

```javascript:API
export const getHogeNextPostsId = ({
  targetFilter,
}) =>
  instance.get('/hoge', {
    params: {
      limit: 1,
      fields: 'id',
      orders: 'publishedAt',
      filters: `publishedAt[greater_than]${targetFilter}`,
    },
  });

export const getHogePrevPostsId = ({
  targetFilter,
}) =>
  instance.get('/hoge', {
    params: {
      limit: 1,
      fields: 'id',
      orders: '-publishedAt',
      filters: `publishedAt[less_than]${targetFilter}`,
    },
  });
```

```javascript:詳細画面のgetStaticProps
// 詳細記事のdata
const params = context.params as Params;
const response = await getHogePosts({ id: params.id });
const { data } = response;

// 次の記事と前の記事のidを取得
const { publishedAt } = data;
const { data: { contents: nextPosts }} = await getHogeNextPostsId({
  targetFilter: publishedAt,
});
const { data: { contents: prevPosts }} = await getHogePrevPostsId({
  targetFilter: publishedAt,
});
```

### 詳細記事にはその記事のサブカテゴリーの関連記事を表示

 - 関連記事に必要な要素をクエリ `fields` で指定
 - クエリ`filters`で詳細記事のサブカテゴリー記事一覧を取得、ただしその詳細記事は除外とする

``` javascript:API
export const getHogeRelatedPosts = ({
  id,
  subCategoryId,
}) =>
  instance.get('/hoge', {
    params: {
      fields: 'id,title,publishedAt,mainVisual',
      filters: `subCategory[equals]${subCategoryId}[and]id[not_equals]${id}`,
    },
  });
```

```javascript:詳細画面のgetStaticProps
// 詳細記事のdata
const params = context.params as Params;
const response = await getHogePosts({ id: params.id });
const { data } = response;

// 関連記事
const { subCategory: { id: subCategoryId }} = data;
const {data: { contents: relatedPosts }} = await getHogeRelatedPosts({
  id: params.id,
  subCategoryId,
});
```

以上となります。
いかがでしたでしょうか。microCMSでブログサイトを開発するときの一助となれば幸いです。