← 解説「キャッシュの考え方 — 取得結果を使い回す」に戻る
サンプルコードで身につける: キャッシュの考え方 — 取得結果を使い回す
解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。
1最小の revalidate
fetch の第2引数に revalidate の秒数を書くだけ、というキャッシュ指定の最小形です。この1行で「60秒間は保存した結果を返し、期限が切れたら取り直す」という動きになり、API への問い合わせ回数が激減します。
// app/news/page.tsx
type NewsItem = { id: number; headline: string };
export default async function NewsPage() {
const res = await fetch("https://api.example.com/news", {
next: { revalidate: 60 }, // 60秒間は結果を使い回す
});
const items: NewsItem[] = await res.json();
return (
<ul>
{items.map((item) => (
<li key={item.id}>{item.headline}</li>
))}
</ul>
);
}
// 60秒以内の再アクセスは API を呼ばずキャッシュが返る。
2no-store で常に最新を取る
在庫数や株価のように一瞬でも古い値を見せたくないデータは、cache: "no-store" でキャッシュを完全に無効化します。「速さより正確さ」を選ぶ指定で、revalidate と並ぶキャッシュ戦略のもう一方の極です。
// app/stock/page.tsx — 在庫数は1秒でも古いと困る
type Stock = { productId: number; remaining: number };
export default async function StockPage() {
const res = await fetch("https://api.example.com/stock", {
cache: "no-store", // 保存せず、毎回必ず取りに行く
});
const stocks: Stock[] = await res.json();
return (
<ul>
{stocks.map((s) => (
<li key={s.productId}>残り {s.remaining} 点</li>
))}
</ul>
);
}
// 「正確さが命」のデータは no-store、が使い分けの基準。
3force-cache でずっと使い回す
都道府県一覧のようにほぼ変わらないデータは、force-cache で一度取得した結果を保存し続けます。revalidate(期限付き)・no-store(保存しない)・force-cache(保存し続ける)の3つで、鮮度の要求に合わせた選択肢が揃います。
// app/signup/page.tsx — 都道府県一覧はめったに変わらない
type Pref = { code: string; label: string };
export default async function SignupPage() {
const res = await fetch("https://api.example.com/prefectures", {
cache: "force-cache", // 一度取ったら保存した結果を使い続ける
});
const prefs: Pref[] = await res.json();
return (
<select>
{prefs.map((p) => (
<option key={p.code}>{p.label}</option>
))}
</select>
);
}
// 3つの使い分け: 期限付き=revalidate / 毎回=no-store / ずっと=force-cache
4期限切れ判定だけを取り出す
revalidate の心臓部である「保存してからの経過時間が期限を超えたか」という判定を、1つの関数に切り出して実行する例です。境界(ちょうど60秒)でどちらに転ぶかも含めて、キャッシュの寿命の感覚を数字で確かめられます。
TypeScript
5ニュースサイトの鮮度設計
1つのページの中でも、データごとに求められる鮮度は違います。速報は60秒、ランキングは1時間、会社情報は1日と、fetch ごとに別の revalidate を指定して鮮度と負荷のバランスを設計する、実務のキャッシュ戦略の縮図です。
// app/page.tsx — データごとに鮮度の要求が違う
export default async function TopPage() {
// 速報: 鮮度が命なので60秒
const flashRes = await fetch("https://api.example.com/flash-news", {
next: { revalidate: 60 },
});
// 週間ランキング: 1時間ごとで十分
const rankingRes = await fetch("https://api.example.com/ranking", {
next: { revalidate: 3600 },
});
// 会社情報: 1日1回で十分
const aboutRes = await fetch("https://api.example.com/company", {
next: { revalidate: 86400 },
});
const flash = await flashRes.json();
const ranking = await rankingRes.json();
const company = await aboutRes.json();
return <main>{/* flash, ranking, company を表示 */}</main>;
}