Dev Study
← 解説「キャッシュの考え方 — 取得結果を使い回す」に戻る

サンプルコードで身につける: キャッシュの考え方 — 取得結果を使い回す

解説で学んだ概念を、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>;
}