Skip to content

Next.js APIルートにおけるNEXT_REDIRECTエラーの解決策

問題の概要

Next.js 13.4以上のバージョンでAPIルート(app/api/**/route.ts)内でredirect関数をtry-catchブロック内で使用した場合、以下のエラーが発生します:

error-log
Error: NEXT_REDIRECT
        at getRedirectError (webpack-internal://...)
        at redirect (webpack-internal://...)
        at GET (webpack-internal://...)

具体的な問題の発生条件は次のコード例の通りです:

ts
import { redirect } from 'next/navigation';

// 👎 問題のある実装
export async function GET(req: Request) {
  try {
    redirect('/dashboard'); // ここでエラー発生
  } catch (error) {
    console.log(error);
    redirect('/');
  }
}

一方で、try-catchブロックを使用しないシンプルなリダイレクトは正常に動作します:

ts
// 👍 正常に動作するリダイレクト
export async function GET(req: Request) {
  redirect('/dashboard');
}

根本原因

このエラーの主な原因は、Next.jsのredirect関数が内部的にエラーをスローする仕様にあるためです:

  1. redirect関数はリダイレクトを実現するためにNEXT_REDIRECTという特殊なエラーをスロー
  2. try-catchブロック内で使用すると、このエラーが通常のエラーとしてキャッチされる
  3. 結果として、リダイレクト処理が中断されコンソールにエラーが表示される

Next.js公式ドキュメントでも、redirect()try/catchブロック外で使用するよう明示されています。

解決方法

APIルートでの適切な方法(推奨)

APIルートではNextResponse.redirectを使用するのが公式推奨の手法です:

ts
import { NextResponse } from 'next/server';

export async function GET(req: Request) {
  try {
    // 認証処理など
    return NextResponse.redirect(new URL('/dashboard', req.url));
  } catch (error) {
    console.error(error);
    return NextResponse.redirect(new URL('/', req.url));
  }
}

重要なポイント

  • NextResponsenext/serverからインポート
  • 絶対URLが必要なためnew URL()でURLオブジェクトを生成
  • returnを使ってレスポンスを返す必要あり

サーバーコンポーネントでの対処法

サーバーコンポーネントでは、redirect関数を使いつつエラー処理を行う必要がある場合、finallyブロックを使う方法が安全です:

ts
import { redirect } from 'next/navigation';

export default async function AuthComponent() {
  let redirectPath = null;

  try {
    // 認証処理...
    redirectPath = '/dashboard';
  } catch (error) {
    console.error(error);
    redirectPath = '/error';
  } finally {
    if (redirectPath) redirect(redirectPath);
  }

  // コンポーネントの通常レンダリング
  return <div>Loading...</div>;
}

高度な方法:リダイレクトエラーの判別

直接エラーをチェックする方法もありますが、内部APIの利用には注意が必要です:

ts
import { redirect } from 'next/navigation';
import { isRedirectError } from 'next/dist/client/components/redirect';

export async function GET(req: Request) {
  try {
    redirect('/dashboard');
  } catch (error) {
    if (isRedirectError(error)) throw error; // リダイレクトエラーを再スロー
    console.error(error);
    redirect('/'); // 通常エラーの場合
  }
}

注意

next/dist/以下のモジュールは非公開APIです。バージョンアップで変更される可能性があり、プロダクションコードでの使用には注意が必要です。

避けるべきパターン

❌ 禁止1:redirectを直接try内で使う

ts
try {
  redirect('/dashboard'); // NG!
} catch (error) {
  // ...
}

❌ 禁止2:redirectの前にreturnしない

ts
try {
  return redirect('/dashboard'); // これもNG!
} catch {
  // ...
}

❌ 禁止3:無効なリダイレクト位置

ts
export async function GET() {
  const doRedirect = () => redirect('/'); // ネスト関数内での使用

  try {
    doRedirect(); // NG!
  } catch {
    // ...
  }
}

補足情報(Next.js 15以降)

Next.js 15ではpermanentRedirect関数が新たに導入されています:

ts
import { permanentRedirect } from 'next/navigation';

export default function Page() {
  permanentRedirect('/profile');
}

redirectとの違い

  • permanentRedirect: 永続リダイレクト(ステータスコード308)
  • redirect: 一時リダイレクト(ステータスコード307)

詳細は公式ドキュメントを参照してください。

まとめ

Next.jsでredirect関数をエラーハンドリングと併用する場合のポイント:

  1. **APIルートではNextResponse.redirect**を使用
  2. サーバーコンポーネントではfinallyブロックでリダイレクト実行
  3. エラーハンドリングが必要ない場合、try-catchの使用は避ける
  4. 非公開API(isRedirectError)には依存しない
  5. Next.js 15+ではpermanentRedirectも検討

これらのベストプラクティスを適用することで、NEXT_REDIRECTエラーを発生させることなく安全なリダイレクト処理を実装できます。