SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Next.jsがApp Routerの"次"に目指すもの

Next.js 15の新機能――ルーティングとキャッシュの制御の変更を中心に解説

Next.jsがApp Routerの"次"に目指すもの 第2回

Next.js 15ではルーティングとキャッシュの制御が変わった

 そんなApp Routerの次の時代を切り開くNext.js 15が2024年10月21日に発表され、続いて、同年12月10日にはNext.js 15.1が発表されました。

 開発体験を向上する魅力的な改善が多く見られますが、特に注目すべきはルーティングとキャッシュに関する破壊的な変更です。

  • リクエスト内容の参照に関するAPIを非同期にする(Async Request APIs)
  • いくつかの挙動でデフォルトをキャッシュなしにする(Caching Semantics)

 破壊的な変更ということで、これまでのApp Routerを使っていたユーザーにとっては心配もあるかと思いますが、 @next/codemodという更新用のCLIが整備されているので、リリースノートを読みながら活用してみてください。

リクエスト内容の参照に関するAPIを非同期にする

 まずは、Server Componentの内部でリクエスト内容を参照するAPIに関する変更です。これは開発体験の向上というより、フレームワーク側の速度向上や最適化を目的とした変更です。変更の対象となるAPIはいくつかあり、関数として提供されるものは次の3つです。

  • cookies
  • headers
  • draftMode

 そして、propsや引数として提供されるものは次の2つです。

  • paramslayout.jspage.jsroute.jsdefault.jsgenerateMetadatagenerateViewport
  • searchParamspage.jsのみ)

 これらの変更後のAPIを利用すると、page.jsはリスト1の書き方になります。

[リスト1]app/async-request-apis/[slug]/page.js
import { headers } from "next/headers";

export default async function AsyncRequestApisPage(props) {
  const _headers = await headers(); // (1)
  const params = await props.params; // (2)
  const searchParams = await props.searchParams; // (3)

  return (
    <main className="container p-10">
      <h1 className="text-2xl font-bold mb-4">Async Request APIs</h1>
      <p>Params: {JSON.stringify(params)}</p>
      <p>Search Params: {JSON.stringify(searchParams)}</p>
      <p>Headers: {JSON.stringify(_headers)}</p>
    </main>
  )
}

 リスト1を実際に手元で動かして、http://localhost:3000/async-request-apis/foobar?query=buz にアクセスすると、図1のような表示になります。

図1:変更されたAPIを動かしてみる
図1:変更されたAPIを動かしてみる

 無事に動作していそうです。(1)は従来はオブジェクトを返す関数でしたが、今回の変更でPromiseを返す関数に変更され、呼び出しには await が必要になりました。また、(2)や(3)のようにURLのパスに含まれるパラメータやSearch Paramsを取得するAPIも、Promiseを返すように変更されています。propsから取り出した値に await を付けるのは、慣れるまでは少し奇妙に感じられそうですね。

 なぜこのような変更が行われたのか見てみましょう。従来はheaders()props.params などを同期的に扱うために、リクエストの到達を待ってからpage.jsなどに紐づくServer Componentの評価を始めていました。しかしながら、リクエスト依存のパラメータを使わなくても評価できるページやレイアウトも多いため、リクエストの到達を待つ時間がオーバーヘッドになっていたようです。

 今回の変更により、リクエストの到達を待たずにServer Componentの評価を始めることができるようになり、たまたまcookiessearchParamsなどを使っていたページだけが評価に少し時間がかかる、といった世界観に変わりました。

 awaitを書く場所が増える点では開発体験は少し悪くなるのですが、Server Componentsを扱い慣れてきたNext.jsチームならではの最適化だと思うので、筆者は好意的に捉えています。

いくつかの挙動でデフォルトをキャッシュなしにする

 さて、もうひとつの破壊的な変更として、いくつかの挙動においてデフォルトでキャッシュしていた挙動がキャッシュしないように変更されたものがあります。ユーザーからのフィードバックとして、キャッシュはオプトインで実装されていたほうが挙動を予測しやすい、という意見があったのではないかと、筆者は推測しています。

 まずは、Route Handler(route.js)で定義しているGET関数のレスポンスを、原則としてキャッシュしないようにする変更です。Next.js 15からは原則として毎回評価されます。export dynamic = 'force-static'を記載することでオプトインでキャッシュを有効にすると、従来通りの挙動になるようです。

 次に、Client Routerの挙動の変更です。Next.js 14.2.0でnext.config.jsにstaleTimesという設定が追加され、動的なコンテンツを含むページ遷移であっても、所定の時間が経過するまではキャッシュを使ってページ遷移を行うようになっていました。デフォルトで30秒間キャッシュされるようになっていましたが、今回からはデフォルトで0秒が設定され、キャッシュを使わず毎回動的にコンテンツを取得するようになりました。

 従来通りの挙動にするためには、リスト2のようにstaleTimesを設定する必要があります。

[リスト2]next.config.js
const nextConfig = {
  experimental: {
    staleTimes: {
      dynamic: 30,
    },
  },
};
 
export default nextConfig;

 そして最後に、リリースノートの記事に書かれていない変更点として、fetch()関数のデフォルトのキャッシュの挙動が変更されています。公式ドキュメントの「Data Fetching and Caching」のページを見てみましょう。

 次のような記載があります。

 > Good to know:

 >

 > In previous versions of Next.js, using fetch would have a default cache value of force-cache. This changed in version 15, to a default of cache: no-store.

 従来はServer Componentを動的レンダリングする際にfetch()関数を実行すると、第一引数のURL文字列をキーにして自動でキャッシュされる挙動になっていましたが、今後はデフォルトでキャッシュしない挙動になります。

 キャッシュしたほうが都合がいい場合もあると思うので、その場合はリスト3のようにcache: 'force-cache'を指定してください。

[リスト3]app/fetch-optin/page.js
export default async function FetchOptIn() {
  const text = await fetch(
    // base64 エンコードした "App Router is AWESOME" という文字列をデコードする
    "https://httpbin.org/base64/QXBwIFJvdXRlciBpcyBBV0VTT01F",
    {
      cache: "force-cache", // (1)
      next: {
        tags: ["my-base64"] // (2)
      }
    }
  ).then((res) => res.text());

  return (
    <main className="container p-10">
      <h1 className="text-2xl font-bold mb-4">Fetch OptIn</h1>
      <p>text: {text}</p>
    </main>
  )
}

 (1)のように指定することで、従来通りの挙動にすることができます。(2)のようにタグをつけて、Server ActionsなどのタイミングでrevalidateTag()でキャッシュを更新する流れは特に変わりません。

 このように、Next.js 15では、暗黙的にキャッシュしていた挙動のいくつかが廃止され、明示的に指定する形に変更されました。キャッシュを有効にする箇所は少なくないと思われるので、開発者が手を動かす量は増えてしまったのかもしれませんが、それでも暗黙的な挙動でトラブルが発生する心労と比べれば、オプトイン方式のほうが安全であるという意見に筆者は同意します。

次のページ
その他の改善点

この記事は参考になりましたか?

Next.jsがApp Routerの"次"に目指すもの連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 中川 幸哉(ナカガワ ユキヤ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。 2026年時点での登録メンバは約50名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるLaravel実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/20763 2025/01/22 11:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー