Skip to content

nextUrl.searchParamsによる静的レンダリングエラーの解決

問題の本質

Next.jsアプリケーションを開発モード(npm run dev)では正常に動作するものの、本番ビルド(npm run build)時に以下のエラーが発生します:

Dynamic server usage: Page couldn't be rendered statically because it used `nextUrl.searchParams`. See more info here: https://nextjs.org/docs/messages/dynamic-server-error

エラーの根本原因は:

  1. Next.jsはルートハンドラをデフォルトで静的生成しようとする
  2. request.nextUrl.searchParams(URL検索パラメータ)はリクエストごとに変化する動的データ
  3. この動的データを直接使用すると静的な事前レンダリングが不可能になる
  4. 特にtry/catchブロック内で検索パラメータを処理すると、Next.jsの内部エラーハンドリングメカニズムと競合する

効果的な解決策

解法1: 動的データをtry/catchブロックの外で抽出(推奨)

request.nextUrl.searchParamsの処理をtryブロックの外で実行し、動的データを明示的に扱う:

javascript
export const GET = async (request) => {
  // 検索パラメータは常にtryブロックの外で取得
  const searchParams = request.nextUrl.searchParams;

  try {
    await connectDB();
    // 関数引数としてsearchParamsを渡す
    const users = await getUsers({ searchParams }); 
    return NextResponse.json(users);
  } catch (error) {
    console.error(error);
    return NextResponse.json({
      apiMessage: { errorMsg: "Internal Server Error" },
    });
  }
};

呼び出し先関数getUsers.jsも変更:

javascript
export const getUsers = async ({ searchParams }) => {
  const email = searchParams.get("email") || "";
  const phone = searchParams.get("phone") || "";
  const id = searchParams.get("id") || "";

  // 以降の処理は同じ...
};

利点:

  • Next.jsが動的データ使用を正常に検出できる
  • アプリケーションロジックに最小限の変更で適用可能
  • 静的生成のメリットを最大限活かせる

解法2: unstable_rethrowによるエラー再スロー(Next.js 15以降)

Next.js固有のエラーをキャッチ後に再送する高度な手法:

javascript
import { unstable_rethrow } from 'next/dist/server/app-render';

export const GET = async (request) => {
  try {
    const { nextUrl } = request;
    await connectDB();
    const users = await getUsers(request);
    return NextResponse.json(users);
  } catch (error) {
    // Next.jsエラーのみ再スロー
    unstable_rethrow(error);
    
    // その他のエラーハンドリング
    console.error(error);
    return NextResponse.json({ errorMsg: "Internal Error" });
  }
};

注意点:

  • Next.js 15以降で導入された実験的機能
  • 他のエラーハンドリングと組み合わせる必要あり
  • 公式ドキュメントでのサポート状況を要確認

代替案の注意点

多くの回答で提案されているexport const dynamic = 'force-dynamic'は暫定的な回避策ですが、デメリットがあります:

javascript
export const dynamic = 'force-dynamic'; // ルートハンドラ先頭に追加

欠点:

  • ルートハンドラ全体が静的生成されない
  • パフォーマンスとキャッシュ効率が低下
  • Next.js 15以降ではルートハンドラが既定で動的になるため非推奨

なぜこのエラーが発生するのか

Next.jsの内部動作メカニズム:

  1. ビルド時に静的生成可能なルートを自動検出
  2. nextUrl.searchParams使用時、内部でDynamicServerErrorを送出
  3. このエラーをキャッチして動的レンダリングに切り替え
  4. try/catchがエラーを握り潰すと自動検出が失敗
<diagram> 開発モード: 検索パラメータ処理 → 正常動作 本番ビルド時: 検索パラメータ処理 → DynamicServerError → [try/catchによるエラー抑制] → 検出失敗 修正後フロー: 検索パラメータ抽出 → 動的データと認識 → 動的レンダリング </diagram>

ベストプラクティス

  1. 検索パラメータ取得の原則
    常にrequest.nextUrl.searchParams処理をtry/catchの外で実行

  2. エラーハンドリングの最適化
    Next.js固有エラーとアプリケーションエラーを区別:

javascript
import { isDynamicServerError } from "next/dist/client/components/hooks-server-context";

try {
  // 動的操作...
} catch (error) {
  if (isDynamicServerError(error)) {
    throw error; // Next.jsにエラー処理を委譲
  }
  // カスタムエラーハンドリング
}
  1. 静的生成と動的レンダリングのバランス
    可能な限り静的生成(SSG)を採用し、動的部分は分離:
javascript
// 静的生成可能な部分
export default function StaticComponent() { /* ... */ }

// 動的データが必要なAPIルート
export const GET = async (request) => { 
  const params = request.nextUrl.searchParams; // 明示的に動的と宣言
}

発展的な対応

複雑なルーティングが必要な場合のアプローチ:

Middlewareを活用したパラメータ処理

javascript
// middleware.js
export function middleware(request) {
  const searchParams = request.nextUrl.searchParams;
  request.searchParams = searchParams; // リクエストオブジェクト拡張
}

// route.js
export const GET = (request) => {
  const email = request.searchParams.get("email"); // 直接アクセス
}

ダイナミックルートセグメントの活用

javascript
// パス: /api/users/[email]/route.js
export const GET = (request, { params }) => {
  const { email } = params; // パスから直接取得
}

まとめ

nextUrl.searchParams関連エラーを解決する核心は:

  1. 動的データ操作をtry/catch外で実行する
  2. Next.jsのエラー検出メカニズムを阻害しない
  3. dynamic = 'force-dynamic'は最終手段として選択的に使用する

これらの原則を適用することで、開発環境と本番環境の動作不一致を解消し、Next.jsの静的生成メリットを維持しながら動的なデータ処理を実現できます。