← Files microCMSARCHIVED FILE
skills/microcms-nextjs/references/data-fetching.md
10.1 KB · Oct 4, 2026 · 12:33 UTC
# microCMS + Next.js データ取得
最終確認: 2026-09-08
仕様の根拠は末尾の公式情報を参照する。設計上の推奨・コード例は本Skillの提案であり、唯一の公式推奨構成を意味しない。
## 目次
- [基本方針](#基本方針)
- [一覧を取得する](#一覧を取得する)
- [詳細を取得する](#詳細を取得する)
- [Dynamic Routes](#dynamic-routes)
- [generateStaticParamsを使う](#generatestaticparamsを使う)
- [全件生成しない選択](#全件生成しない選択)
- [ページネーション](#ページネーション)
- [クエリを組み立てる](#クエリを組み立てる)
- [404とエラーを区別する](#404とエラーを区別する)
- [重複取得を避ける](#重複取得を避ける)
- [Client Componentが必要なケース](#client-componentが必要なケース)
- [大量ページのビルドで429になる場合](#大量ページのビルドで429になる場合)
- [公式情報](#公式情報)
## 基本方針
App Routerでは、microCMSデータをServer Componentから取得する方法を基本にする。
取得方法は次の3点を分けて考える。
1. 何を取得するか: 一覧 / 詳細 / オブジェクト
2. いつ取得するか: build / request / revalidation
3. どこまでキャッシュするか
このファイルでは主に「何を取得するか」を扱う。キャッシュは `caching.md` で決める。以下のページ例をCache Components有効のプロジェクトへ組み込む場合、公開データを `use cache` で囲むか、未キャッシュの取得を行う子コンポーネントを `<Suspense>` 配下に置く。
## 一覧を取得する
```ts
import { client } from '@/lib/microcms'
import type { Article } from '@/types/article'
export async function getArticles() {
return client.getList<Article>({
endpoint: 'articles',
queries: {
orders: '-publishedAt',
limit: 20,
},
})
}
```
ページ側:
```tsx
export default async function Page() {
const data = await getArticles()
return (
<main>
{data.contents.map((article) => (
<article key={article.id}>
<h2>{article.title}</h2>
</article>
))}
</main>
)
}
```
一覧で本文が不要なら `fields` を使う。
```ts
queries: {
fields: ['id', 'title', 'eyecatch', 'publishedAt'],
}
```
表示に使わない大きなフィールドを毎回取得しない。
## 詳細を取得する
```ts
export async function getArticle(id: string) {
return client.getListDetail<Article>({
endpoint: 'articles',
contentId: id,
})
}
```
コンテンツIDをURLとして使えるなら、そのままDynamic Segmentに利用できる。
slugフィールドを別に設けている場合は、`filters` で検索する方法もある。ただし「詳細1件取得のために毎回一覧検索する」設計になるため、コンテンツIDをURLに使える要件なら `getListDetail` の方が単純。
## Dynamic Routes
Next.jsの現行App Routerでは `params` がPromiseとして扱われるバージョンがあるため、プロジェクトのNext.jsバージョンに合わせる。
Next.js 16系の例:
```tsx
import { notFound } from 'next/navigation'
import { isMicroCMSRequestError } from 'microcms-js-sdk'
import { client } from '@/lib/microcms'
import type { Article } from '@/types/article'
export default async function Page({
params,
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params
const article = await client.getListDetail<Article>({
endpoint: 'articles',
contentId: id,
}).catch((error: unknown) => {
if (isMicroCMSRequestError(error) && error.status === 404) {
notFound()
}
throw error
})
return <article>{article.title}</article>
}
```
この例は `isMicroCMSRequestError` をexportするSDKが前提。導入済みバージョンのexport・型を確認する。401・403・429・5xx・通信障害は再throwし、Next.jsのエラー処理へ渡す。404が続く場合はエンドポイント名の誤りも確認する。
## generateStaticParamsを使う
ビルド時に静的生成したいDynamic Routesでは `generateStaticParams` を使える。
```ts
export async function generateStaticParams() {
const ids = await client.getAllContentIds({
endpoint: 'articles',
})
return ids.map((id) => ({ id }))
}
```
利用前に次を確認する。
- コンテンツ件数は何件か。
- 全件を毎ビルド生成する必要があるか。
- ビルド時間は許容できるか。
- 新規コンテンツをビルドなしで生成したいか。
数千・数万ページを無条件に全件 `generateStaticParams` へ渡すことをデフォルトにしない。
`generateStaticParams` はISRの再検証時には再実行されない。新規IDの表示には未生成パスを扱える構成が必要で、従来モデルの `dynamicParams = false` は未生成パスを404にする。Cache Components有効時にこの関数を定義する場合は1件以上を返す必要があり、空のCMSから返る `[]` でビルドが失敗しうる。架空のIDで隠す前に、事前生成が必要かを判断する。
## 全件生成しない選択
大量コンテンツでは、ビルド時に一部だけ生成し、残りはアクセス時に生成する方が適する場合がある。
Next.jsのキャッシュモデル・`dynamicParams`・Cache Componentsの有無によって具体的な挙動が異なるため、`caching.md` と現在のNext.js公式仕様を確認する。
判断基準:
- アクセスの大半が上位100記事に偏る → 上位だけ事前生成を検討
- すべてのページに同程度のアクセス → 全件生成も検討
- ビルド時間が長い → 事前生成数を減らす
- 初回アクセスの遅延を絶対避けたい → 事前生成範囲を広げる
## ページネーション
microCMSの `limit` / `offset` を使う。
```ts
const PER_PAGE = 20
export async function getArticles(page: number) {
if (!Number.isSafeInteger(page) || page < 1) {
throw new RangeError('page must be a positive safe integer')
}
const offset = (page - 1) * PER_PAGE
if (!Number.isSafeInteger(offset)) {
throw new RangeError('offset exceeds the safe integer range')
}
return client.getList<Article>({
endpoint: 'articles',
queries: {
limit: PER_PAGE,
offset,
orders: '-publishedAt',
},
})
}
```
ページ数:
```ts
const totalPages = Math.ceil(data.totalCount / PER_PAGE)
```
すべてのコンテンツを取得してフロントエンドでsliceする方法は、件数が少ない特別なケース以外では避ける。
## クエリを組み立てる
ユーザー入力を `filters` 文字列へ直接連結しない。検索・カテゴリ等の要件に応じて許可する条件を明確にする。
例:
```ts
export async function getArticlesByCategory(categoryId: string) {
// この例で扱うコンテンツIDの許可文字。既存APIのIDも確認する。
if (!/^[A-Za-z0-9_-]+$/.test(categoryId)) {
throw new Error('Invalid category ID')
}
return client.getList<Article>({
endpoint: 'articles',
queries: {
filters: `category[equals]${categoryId}`,
fields: ['id', 'title', 'publishedAt'],
},
})
}
```
microCMSの `filters` には値をエスケープする仕様がない。URLエンコードしても、復号後の値に含まれるフィルター構文を無効化できない。IDは許可文字で検証し、自由文検索なら `q` など要件に適した方法を選ぶ。
複雑なクエリをページコンポーネント内へ散在させず、再利用される取得条件はデータ取得関数へまとめる。
## 404とエラーを区別する
microCMS APIの失敗はすべて404ではない。
区別する例:
- 404: 対象コンテンツが存在しない → `notFound()` 候補
- 401 / 403: APIキーや権限の問題 → 設定エラーとして扱う
- 429: レート制限等 → リトライ・取得量確認
- 5xx / ネットワーク: 一時障害 → 404にしない
`microcms-js-sdk` の `isMicroCMSRequestError` を使うと、`status` 等を確認できる。
詳細なSDKエラー仕様は `microcms-guide` の `js-sdk.md` を使う。
## 重複取得を避ける
App Routerでは、同じデータを複数コンポーネントから使うことがある。
まず以下を検討する。
- 親Server Componentで1回取得してpropsで渡す。
- 取得関数を共有する。
- Next.jsの現在のキャッシュ・メモ化機構を利用する。
ただし、古いNext.jsの「同一fetchは必ず自動memoizeされる」などの知識を無条件に前提としない。利用中バージョンの仕様を確認する。
## Client Componentが必要なケース
以下はClient Componentが適することがある。
- 入力中の検索候補を都度取得する。
- ユーザー操作に応じて追加データをロードする。
- ブラウザAPIと連動する。
それでもAPIキーをクライアントへ出したくない場合は、Route Handlerを介す。
一方、通常の記事一覧・詳細表示だけならServer Componentを優先する。
## 大量ページのビルドで429になる場合
ページ生成・全件取得・複数ワーカーの並列実行が重なっていないか確認し、事前生成数や取得の同時実行数を調整する。microCMS公式ヘルプのNext.js向け `experimental.cpus` や `staticGenerationMaxConcurrency` 等は、対応バージョンや実験的機能の条件を確認して使う。ヘルプの例を全Next.jsプロジェクトに追加するデフォルトにしない。[公式ヘルプ:429とビルド並列数](https://help.microcms.io/ja/knowledge/handling-429-errors)
## 公式情報
- [microCMS JavaScript SDK](https://github.com/microcmsio/microcms-js-sdk)
- [microCMSのクエリ仕様](https://document.microcms.io/content-api/get-list-contents)
- [コンテンツIDの設定](https://document.microcms.io/manual/content-id-setting)
- [generateStaticParams](https://nextjs.org/docs/app/api-reference/functions/generate-static-params)
- [Cache ComponentsとSuspense](https://nextjs.org/docs/messages/blocking-route)
SHA-256: eda576841e292d0963026cfa01ecc822e8c77e9ad507d74c8fe0892759aeba22