Next.js 15 へのアップグレード前にできるコード書き換え

2024/12/18に公開

2024.10.24頃、Next.js Version 15 がリリースされました。
さらに 12.11頃には、15.1 もリリースされたので、プロジェクトの Next.js の Version 14 からのアップグレードの検討を始めました。

公式ドキュメントの通り Breaking change が多いので、Next.js のアップグレードと同時にコードの書き換えも多く必要です。
できれば同時の書き換えを最小限に抑えたいと考えていたところ、Async Request APIs に関しては、Next.js Version 14 のまま先に書き換えすることが可能だったので、アップグレード前に対応しました。
その書き換え内容を紹介します。

Page, Layout の params, searchParams

公式ドキュメントの書き換え方法

それぞれ Promise を返すようになります。
Params, SearchParams 型は自分で用意する必要があるので、既存コードの型に Promise<> を追加するだけで済みます。
書き換え方法はそのまんま公式ドキュメント通りなので省略します。

注意事項

Version 14 では同期的な値、Version 15 では Promise になるため、MaybePromise 型を作ってみたのですが、Version 15 でビルドが失敗しました。
最初から思い切って Promise にしましょう。

// Version 14 での元のコード
type Params = { slug: string }
type SearchParams = { [key: string]: string | string[] | undefined }

// Version 15 にするとビルドが失敗する書き方
type Params = MaybePromise<{ slug: string }>
type SearchParams = MaybePromise<{ [key: string]: string | string[] | undefined }>
type MaybePromise<T> = T | Promise<T>

// Version 15 にしてもビルドが失敗しない書き方
type Params = Promise<{ slug: string }>
type SearchParams = Promise<{ [key: string]: string | string[] | undefined }>
# ビルド失敗のエラー内容
Type error: Type '***' does not satisfy the constraint 'PageProps'.
  Types of property 'params' are incompatible.
    Type 'MaybePromise<Params<Record<string, never>>>' is not assignable to type 'Promise<any> | undefined'.
      Type 'Params<Record<string, never>>' is not assignable to type 'Promise<any> | undefined'.
        Type 'Params<Record<string, never>>' is missing the following properties from type 'Promise<any>': then, catch, finally, [Symbol.toStringTag]

next/headerscookies, headers, draftMode

それぞれ Promise を返すようになります。
これらは用意されたメソッドを利用するだけなので、自分で型を用意していることはないはずです。
提供されている型を上書きすることで対応します。

// src/next-15.d.ts

import { ReadonlyRequestCookies } from 'next/dist/server/web/spec-extension/adapters/request-cookies';
import { ReadonlyHeaders } from 'next/dist/server/web/spec-extension/adapters/headers';
import { DraftMode } from 'next/dist/client/components/draft-mode';

declare module 'next/headers' {
  export function cookies(): Promise<ReadonlyRequestCookies>;
  export function headers(): Promise<ReadonlyHeaders>;
  export function draftMode(): Promise<DraftMode>;
}

あとは cookies(), headers(), draftMode()await をつけるだけです。

まとめ

Next.js Version 14 のまま、Version 15 の型を先取りすることで、アップグレード時の実装の変更を最小限にできます。
特に今携わっている Webアプリケーションのプロジェクトでは、Dynamic Routes が山ほどあることや、メインの開発も止めたくないことから、事前書き換えは非常に役立ちました。

プロジェクトの Next.js Version 15 アップグレードの PR は、今や config.experimental.serverComponentsExternalPackagesconfig.serverExternalPackages への書き換えと、tsconfig.json の自動更新程度です。

今回のプロジェクトでは何の問題もなく対応できましたが、必ず動作確認を忘れないように。
それでは楽しい Next.js ライフを。

Discussion