
Next.js 15の非同期Request APIとキャッシュポリシーの変更:何が変わり、どう対応すべきか
Next.jsはバージョン15へのアップデートに伴い、サーバーコンポーネントとルートハンドラーの扱い方に大きな変更を加えました。これまで当然のように同期的に読み取っていたクッキーやヘッダー、URLパラメータが非同期APIへと転換され、開発者の間で好みが分かれていたデフォルトのfetchキャッシュポリシーも完全に刷新されました。
App Router環境でWebサービスを開発してきたチームであれば、マイグレーションの過程でビルドエラーやランタイム警告に直面することになるでしょう。どのような背景でこの変化が生まれたのか、実際のコードの書き方がどう変わるのかを整理します。
何が変わったのか
主な変更点は2つです。1つ目はリクエスト関連オブジェクトへのアクセスがPromiseを返すようになった点、2つ目はfetchリクエストおよびGETルートハンドラーのデフォルトキャッシュが無効化された点です。
これまでのNext.js 13や14では、cookies()やheaders()を同期関数のように呼び出して、値を直接取り出していました。ページやレイアウトコンポーネントが受け取るparamsとsearchParamsプロップも、ごく一般的なオブジェクトでした。Next.js 15からはこれら全てがPromiseベースで動作するため、awaitを付与して取得する必要があります。
キャッシュについては、Next.js 14まではfetchリクエスト時に開発者が特別な設定をしなければ、自動的にデータを永続的にキャッシュ(force-cache)していました。このため、データがリアルタイムで更新されず混乱する開発者が多くいました。Next.js 15はこの動作を廃止し、Web標準と同じように毎リクエストで新しく取得するno-storeをデフォルト値に設定しました。
なぜ同期APIを非同期に変えたのか
一見、コードにawaitをもう一つ追加しなければならず、手間に感じるかもしれません。Vercelチームがこのインターフェースを全面的に修正した理由は、サーバーレンダリングの最適化とストリーミング効率のためです。
サーバーがページレンダリングを開始する際、特定のコンポーネントがクッキーやヘッダーを読み取ろうとすると、従来の構造では実際のリクエストヘッダーが完全に到着するまでサーバーレンダリング全体が待機状態に陥っていました。リクエスト情報が非同期に変われば、フレームワークはリクエストヘッダーが完全に評価される前であっても、静的なレイアウトやデータを必要としない上位ツリーを先にストリーミングすることが可能になります。
静的レンダリングと動的レンダリングの境界を明確に区別しようという意図も強いです。非同期ハンドラーを使用すると、ランタイム時にどのコンポーネントがリクエストコンテキストに依存しているかを正確に追跡できるため、今後導入されるコンパイラや部分的な事前レンダリング最適化の土台となります。
実際のコードの書き方:cookies()とparams
Next.js 15環境でクッキーを読み取ったり、ダイナミックルートのパラメータを扱ったりするには、呼び出し元にawaitを明示する必要があります。
Next.js 15では従来の方法でconst \{ id \} = params;のように直接分割代入を試みると、開発モードでランタイム警告が出力されます。公式のマイグレーションコードモッドを実行すれば、既存コンポーネントのパラメータやヘッダーの呼び出し箇所にawaitを一括適用できます。
fetchデフォルト値の変更とキャッシュ対応戦略
デフォルトのキャッシュが無効化されたため、これからのNext.jsのfetchは、ブラウザのネイティブ動作と同じように、毎回バックエンドサーバーへ新しいリクエストを送信します。ルートハンドラーのGETリクエストも静的に固定されず、毎回実行されます。
頻繁に変化しない外部APIデータを呼び出す場合や、以前のように強力な静的キャッシュを維持したい場合は、明示的にオプションを指定する必要があります。
リアルタイム性が重要なダッシュボードや会員専用ページでは、これまでのように毎回\{ cache: 'no-store' \}を付与する必要がなくなるため、意図しないキャッシュ汚染バグが大幅に減少します。一方で、頻繁に呼び出される共通のマスターデータやコンテンツ的な性質のページを運用している場合は、キャッシュミスによりバックエンドのトラフィックが増加する可能性があるため、force-cacheオプションが必要かどうかを確認することをお勧めします。
