Menu

use cache: private

'use cache: private'ディレクティブは、クッキー、ヘッダー、またはサーチパラメータに依存する個人用コンテンツのランタイムプリフェッチを有効にします。

補足: 'use cache: private'は、ユーザー固有のコンテンツ用に設計されたuse cacheのバリアントで、プリフェッチ可能である必要がありますが、サーバー側のキャッシュハンドラに保存されることはありません。

使用方法

'use cache: private'を使用するには、next.config.tsファイルでcacheComponentsフラグを有効にします:

next.config.ts
TypeScript
import type { NextConfig } from 'next'
 
const nextConfig: NextConfig = {
  cacheComponents: true,
}
 
export default nextConfig

次に、'use cache: private'cacheLife設定と共に関数に追加し、ページからunstable_prefetchをエクスポートします。

基本的な例

app/product/[id]/page.tsx
TypeScript
import { Suspense } from 'react'
import { cookies } from 'next/headers'
import { cacheLife, cacheTag } from 'next/cache'
 
// 必須:ランタイムプリフェッチを有効にする
export const unstable_prefetch = {
  mode: 'runtime',
  samples: [
    { params: { id: '1' }, cookies: [{ name: 'session-id', value: '1' }] },
  ],
}
 
export default async function ProductPage({
  params,
}: {
  params: Promise<{ id: string }>
}) {
  const { id } = await params
 
  return (
    <div>
      <ProductDetails id={id} />
      <Suspense fallback={<div>Loading recommendations...</div>}>
        <Recommendations productId={id} />
      </Suspense>
    </div>
  )
}
 
async function Recommendations({ productId }: { productId: string }) {
  const recommendations = await getRecommendations(productId)
 
  return (
    <div>
      {recommendations.map((rec) => (
        <ProductCard key={rec.id} product={rec} />
      ))}
    </div>
  )
}
 
async function getRecommendations(productId: string) {
  'use cache: private'
  cacheTag(`recommendations-${productId}`)
  cacheLife({ stale: 60 }) // ランタイムプリフェッチには最小30秒が必要
 
  // プライベートキャッシュ関数内でクッキーにアクセス
  const sessionId = (await cookies()).get('session-id')?.value || 'guest'
 
  return getPersonalizedRecommendations(productId, sessionId)
}

注意: プライベートキャッシュは、ランタイムプリフェッチを有効にするために、最小30秒のstale timeをcacheLifeに設定する必要があります。30秒未満の値は動的として扱われます。

use cacheとの違い

通常のuse cacheは、サーバーでキャッシュできる静的で共有されたコンテンツ用に設計されていますが、'use cache: private'は、以下の条件を満たす必要がある動的でユーザー固有のコンテンツ専用です:

  1. 個人用 - クッキー、ヘッダー、またはサーチパラメータに基づいて変動
  2. プリフェッチ可能 - ユーザーがページにナビゲートする前に読み込み可能
  3. クライアント限定 - サーバー側のキャッシュハンドラに永続化されない
機能use cache'use cache: private'
await cookies()へのアクセスいいえはい
await headers()へのアクセスいいえはい
await searchParamsへのアクセスいいえはい
キャッシュハンドラに保存はい(サーバー側)いいえ(クライアント側のみ)
ランタイムプリフェッチ可能N/A(既に静的)はい(設定時)
キャッシュスコープグローバル(共有)ユーザーごと(分離)
ユースケース静的で共有されたコンテンツ個人用のユーザー固有のコンテンツ

動作方法

ランタイムプリフェッチ

ユーザーがunstable_prefetch = { mode: 'runtime' }のあるページへのリンクにホバーするか表示する場合:

  1. 静的コンテンツが即座にプリフェッチされます(レイアウト、ページシェル)
  2. プライベートキャッシュ関数がユーザーの現在のクッキー/ヘッダーで実行されます
  3. 結果が保存されます(クライアント側の Resume Data Cache)
  4. ナビゲーションが即座になります - 静的と個人用コンテンツの両方が既に読み込まれています

補足: 'use cache: private'がない場合、個人用コンテンツはプリフェッチできず、ナビゲーション完了後に待つ必要があります。ランタイムプリフェッチは、ユーザーの現在のリクエストコンテキストでキャッシュ関数を実行することにより、この遅延を排除します。

ストレージの動作

プライベートキャッシュは、サーバー側のキャッシュハンドラ(Redis、Vercel Data Cache など)に決して永続化されません。これらは以下の目的でのみ存在します:

  1. 個人用コンテンツのランタイムプリフェッチを有効にする
  2. セッション中にプリフェッチされたデータをクライアント側キャッシュに保存する
  3. タグとstale timeでキャッシュの無効化を調整する

これにより、高速でプリフェッチされたナビゲーションを実現しながら、ユーザー固有のデータがユーザー間で誤って共有されることはありません。

Stale timeの要件

注意: cacheLife stale timeが30秒未満の関数は、'use cache: private'を使用していても、ランタイムプリフェッチされません。これにより、ナビゲーション時に古い可能性のある急速に変化するデータのプリフェッチを防ぎます。

// ランタイムプリフェッチされます(stale ≥ 30s)
cacheLife({ stale: 60 })
 
// ランタイムプリフェッチされます(stale ≥ 30s)
cacheLife({ stale: 30 })
 
// ランタイムプリフェッチされません(stale < 30s)
cacheLife({ stale: 10 })

プライベートキャッシュで許可されているリクエストAPI

以下のリクエスト固有のAPIは、'use cache: private'関数内で使用できます:

APIuse cacheで許可'use cache: private'で許可
cookies()いいえはい
headers()いいえはい
searchParamsいいえはい
connection()いいえいいえ

注意: connection() APIは、use cache'use cache: private'の両方で禁止されています。これは、安全にキャッシュできない接続固有の情報を提供するためです。

ネスティングルール

プライベートキャッシュには、ユーザー固有のデータが共有キャッシュに漏れることを防ぐための特定のネスティングルールがあります:

  • プライベートキャッシュは、他のプライベートキャッシュ内にネストできます
  • プライベートキャッシュは、公開キャッシュ('use cache''use cache: remote')内にネストできません
  • 公開キャッシュは、プライベートキャッシュ内にネストできます
// 有効:プライベート内のプライベート
async function outerPrivate() {
  'use cache: private'
  const result = await innerPrivate()
  return result
}
 
async function innerPrivate() {
  'use cache: private'
  return getData()
}
 
// 無効:公開内のプライベート
async function outerPublic() {
  'use cache'
  const result = await innerPrivate() // エラー!
  return result
}
 
async function innerPrivate() {
  'use cache: private'
  return getData()
}

個人用商品推奨

この例は、ユーザーのセッションクッキーに基づいて個人用商品推奨をキャッシュする方法を示しています。ユーザーが商品リンクにホバーする時、推奨がランタイムでプリフェッチされます。

app/product/[id]/page.tsx
import { cookies } from 'next/headers'
import { cacheLife, cacheTag } from 'next/cache'
 
export const unstable_prefetch = {
  mode: 'runtime',
  samples: [
    { params: { id: '1' }, cookies: [{ name: 'user-id', value: 'user-123' }] },
  ],
}
 
async function getRecommendations(productId: string) {
  'use cache: private'
  cacheTag(`recommendations-${productId}`)
  cacheLife({ stale: 60 })
 
  const userId = (await cookies()).get('user-id')?.value
 
  // ユーザーの閲覧履歴に基づいて個人用推奨を取得
  const recommendations = await db.recommendations.findMany({
    where: { userId, productId },
  })
 
  return recommendations
}

ユーザー固有の価格設定

ユーザー階層によって異なる価格設定情報をキャッシュし、個人用料金が既に読み込まれた状態で価格設定ページへの即座なナビゲーションを実現します。

app/pricing/page.tsx
import { cookies } from 'next/headers'
import { cacheLife } from 'next/cache'
 
export const unstable_prefetch = {
  mode: 'runtime',
  samples: [{ cookies: [{ name: 'user-tier', value: 'premium' }] }],
}
 
async function getPricing() {
  'use cache: private'
  cacheLife({ stale: 300 }) // 5分
 
  const tier = (await cookies()).get('user-tier')?.value || 'free'
 
  // 階層固有の価格設定を返す
  return db.pricing.findMany({ where: { tier } })
}

ヘッダーに基づくローカライズされたコンテンツ

ユーザーのAccept-Languageヘッダーに基づいてローカライズされたコンテンツを配信し、ナビゲーション前に正しい言語バリアントをプリフェッチします。

app/page.tsx
import { headers } from 'next/headers'
import { cacheLife, cacheTag } from 'next/cache'
 
export const unstable_prefetch = {
  mode: 'runtime',
  samples: [{ headers: [{ name: 'accept-language', value: 'en-US' }] }],
}
 
async function getLocalizedContent() {
  'use cache: private'
  cacheTag('content')
  cacheLife({ stale: 3600 }) // 1時間
 
  const headersList = await headers()
  const locale = headersList.get('accept-language')?.split(',')[0] || 'en-US'
 
  return db.content.findMany({ where: { locale } })
}

ユーザー設定での検索結果

ユーザー固有の設定を含む検索結果をプリフェッチし、個人用検索結果が即座に読み込まれるようにします。

app/search/page.tsx
import { cookies } from 'next/headers'
import { cacheLife } from 'next/cache'
 
export const unstable_prefetch = {
  mode: 'runtime',
  samples: [
    {
      searchParams: { q: 'laptop' },
      cookies: [{ name: 'preferences', value: 'compact-view' }],
    },
  ],
}
 
async function getSearchResults(query: string) {
  'use cache: private'
  cacheLife({ stale: 120 }) // 2分
 
  const preferences = (await cookies()).get('preferences')?.value
 
  // ユーザー設定を検索結果に適用
  return searchWithPreferences(query, preferences)
}

補足:

  • プライベートキャッシュは短命で、セッション期間中のみクライアント側キャッシュに存在します
  • プライベートキャッシュ結果は、サーバー側のキャッシュハンドラに決して書き込まれません
  • unstable_prefetchエクスポートは、ランタイムプリフェッチが機能するために必須です
  • プライベートキャッシュがプリフェッチされるには、最小30秒のstale timeが必要です
  • cacheTag()revalidateTag()を使用して、プライベートキャッシュを無効化できます
  • 各ユーザーは、クッキー/ヘッダーに基づいて独自のプライベートキャッシュエントリを取得します

プラットフォームサポート

デプロイオプションサポート
Node.jsサーバーはい
Dockerコンテナはい
静的エクスポートいいえ
アダプターはい

バージョン履歴

バージョン変更
v16.0.0'use cache: private'が実験的機能として導入時期:実験的機能。