個人開発でNext.js × microCMSのブログを作成時に「カテゴリに紐づいた記事数を併記したカテゴリ一覧」を実装する機会がありました。
その際に行った内容を備忘録として記載します。
※今回の記事ではmicrocms-js-sdkの活用を想定しています。
前提
- microCMS上で、ブログ(記事)とカテゴリで別々のAPIコンテンツを作成している
- ブログAPI内にカテゴリと紐づいた項目(スキーマ)を作成済である
実装したい内容
下記のように、記事が1つでも登録されているカテゴリの一覧を表示させます。
カテゴリ名の横に記事数も表示させます。
実装内容をブレイクダウンすると、
-
カテゴリに登録されているデータのうち、1件でも記事が登録されているものをリスト形式で表示する
- 登録されている記事がないカテゴリは出力しない
- 記事のうち同じカテゴリが登録されているものの数を取得し、その数を出力する
- 同じカテゴリの記事が複数件存在していたとしても、カテゴリの名称は一度だけ出力する
といった形になります。
実装方針
実際のコードを記載する前に、どのような方針で実装すれば良いかをまとめてみます。
カテゴリ情報の取得
これはカテゴリのデータをそのまま取得すれば問題なさそうです。
カテゴリのデータの取得はmicroCMSからAPIを叩くことで取得できます。
カテゴリに紐づいた記事数の取得
流れとしては
- カテゴリデータの配列を
mapなどで1つずつ参照する - 個別のカテゴリを対象として紐づいた記事の情報を取得する
- カテゴリ側にカウンターを仕込んでおき、全ての記事を取得できたらカウンターの値を「記事数」として出力する
という形になります。
microCMSでカテゴリなど特定の条件に合ったデータを抽出するにはfilterというクエリパラメータが使えます。
また、APIに含まれるデータの総数を取得するにはtotalCountというレスポンスボディが使えます。
実際のコード
カテゴリ情報の取得
これはmicrocms-js-sdkのclient.getを使って容易に作成できます。
async function getCategories(): Promise<Props[]> {
const data = await client.get({
endpoint: 'category',
queries: {
limit: 10,
}
});
return data.contents;
}
カテゴリに紐づいた記事数の取得
先述したように、「カテゴリ配列からデータを1つずつ参照」「個別カテゴリに紐づいた記事の取得」「カウンター変数の設定と値の増加」が必要になります。
今回はこのようなコードで実装しました。
export default async function CategoryAccordionInner() {
const dataContents = await getCategories();
const categoriesWithCount = await Promise.all(
dataContents.map(async (category) => {
const res = await client.get({
endpoint: 'blogs',
queries: {
filters: `category[contains]${category.id}`,
},
});
return { ...category, count: res.totalCount }
})
);
const categoriesHasEntry = await categoriesWithCount.filter((cat) => cat.count != 0)
// 以下略
カテゴリの取得はawaitを用いて非同期で行っていますが、mapでの配列の個別処理はそのままだとうまくいかないため、Promise.all()でmapを囲むという方法を取りました。
const categoriesWithCount = await Promise.all(
dataContents.map(async (category) => {
こうすることで各要素に対する非同期処理をmapでも行えるようになります。
今回取得したい値は「カテゴリのデータ+紐づいた記事数」です。なので、以下のようにすることで元のカテゴリのデータに記事数のプロパティを追加しました。
dataContents.map(async (category) => {
const res = await client.get({
endpoint: 'blogs',
queries: {
filters: `category[contains]${category.id}`,
},
});
// ここで記事数を格納するプロパティを追加
return { ...category, count: res.totalCount }
})
最後に、新たに取得した記事の配列に対して「記事数が0でないもの」を対象にfilterをかければ記事が紐づいたカテゴリのみを抽出できることになります。
const categoriesHasEntry =
await categoriesWithCount.filter((cat) => cat.count != 0);
データの出力
ここまでの作業で記事数が登録されたカテゴリの配列を取得できているはずなので、return()の内側で当該変数を対象にmapで出力すれば問題ありません。
※最低限のロジック表示が目的のため各種classNameは省略しています。
return (
<ul>
{categoriesHasEntry.map((cat) => {
return (
<li key={cat.id}>
<div>
// カテゴリ名とカテゴリ記事数を表示
<p>{cat.title}</p>
<p>{cat.count}</p>
</div>
</li>
)
})}
</ul>
)
最後に
カテゴリ数が紐づいたカテゴリ一覧を作成できれば、ブログサイトとしての完成度が上がります。
この記事が皆様が実装に詰まった時の助けになれば幸いです。

