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
エラーの根本原因は:
- Next.jsはルートハンドラをデフォルトで静的生成しようとする
request.nextUrl.searchParams
(URL検索パラメータ)はリクエストごとに変化する動的データ- この動的データを直接使用すると静的な事前レンダリングが不可能になる
- 特に
try/catch
ブロック内で検索パラメータを処理すると、Next.jsの内部エラーハンドリングメカニズムと競合する
効果的な解決策
解法1: 動的データをtry/catchブロックの外で抽出(推奨)
request.nextUrl.searchParams
の処理をtry
ブロックの外で実行し、動的データを明示的に扱う:
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
も変更:
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固有のエラーをキャッチ後に再送する高度な手法:
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'
は暫定的な回避策ですが、デメリットがあります:
export const dynamic = 'force-dynamic'; // ルートハンドラ先頭に追加
欠点:
- ルートハンドラ全体が静的生成されない
- パフォーマンスとキャッシュ効率が低下
- Next.js 15以降ではルートハンドラが既定で動的になるため非推奨
なぜこのエラーが発生するのか
Next.jsの内部動作メカニズム:
- ビルド時に静的生成可能なルートを自動検出
nextUrl.searchParams
使用時、内部でDynamicServerError
を送出- このエラーをキャッチして動的レンダリングに切り替え
try/catch
がエラーを握り潰すと自動検出が失敗
ベストプラクティス
検索パラメータ取得の原則
常にrequest.nextUrl.searchParams
処理をtry/catch
の外で実行エラーハンドリングの最適化
Next.js固有エラーとアプリケーションエラーを区別:
import { isDynamicServerError } from "next/dist/client/components/hooks-server-context";
try {
// 動的操作...
} catch (error) {
if (isDynamicServerError(error)) {
throw error; // Next.jsにエラー処理を委譲
}
// カスタムエラーハンドリング
}
- 静的生成と動的レンダリングのバランス
可能な限り静的生成(SSG)を採用し、動的部分は分離:
// 静的生成可能な部分
export default function StaticComponent() { /* ... */ }
// 動的データが必要なAPIルート
export const GET = async (request) => {
const params = request.nextUrl.searchParams; // 明示的に動的と宣言
}
発展的な対応
複雑なルーティングが必要な場合のアプローチ:
Middlewareを活用したパラメータ処理
// 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"); // 直接アクセス
}
ダイナミックルートセグメントの活用
// パス: /api/users/[email]/route.js
export const GET = (request, { params }) => {
const { email } = params; // パスから直接取得
}
まとめ
nextUrl.searchParams
関連エラーを解決する核心は:
- 動的データ操作を
try/catch
の外で実行する - Next.jsのエラー検出メカニズムを阻害しない
dynamic = 'force-dynamic'
は最終手段として選択的に使用する
これらの原則を適用することで、開発環境と本番環境の動作不一致を解消し、Next.jsの静的生成メリットを維持しながら動的なデータ処理を実現できます。