TL;DR

Next.js の App Router で Web API を作る仕組み。Route Segment のフォルダに route.ts を置き、HTTP メソッドに対応する関数を export する。

なぜ必要か

主な用途は、ブラウザからの HTTP リクエストの処理である。Cookie からのログインユーザの参照を Next.js に任せ、コンポーネントはエンドポイントを呼ぶだけで済む。

仕組み

export する関数の名前が HTTP メソッドに対応する。使えるメソッドは GET、POST、PUT、PATCH、DELETE、HEAD、OPTIONS である。リクエストされたメソッドが export されていなければ、405 Method Not Allowed を自動で返す。

Route Handler は Web 標準の Fetch API に準拠している。関数が受け取る NextRequest は Fetch API の Request に、返す NextResponse は Response に準拠する。ステータスコードは、NextResponse.json() の第 2 引数で指定できる。

if (!session) {
  return NextResponse.json({ message: "Unauthorized" }, { status: 401 })
}

Static と Dynamic

Route Handler にも、Server Rendering Strategies と同じく Static と Dynamic があり、ビルド時に決まる。

Static Route Handler は、レスポンスをキャッシュファイルとして出力し、複数のリクエストで使い回す(Next.js による fetch の拡張)。

Next.js 14 までは Static がデフォルトだった。Dynamic Segment や cookies() などの Dynamic Functions を使う、GET で Request を参照する、GET 以外を export する、dynamic に "force-dynamic" を指定する、のどれかで Dynamic になった。

Next.js 15 からは Dynamic がデフォルトになり、キャッシュされなくなった。GET をキャッシュするには、Route Segment Config で export const dynamic = 'force-static' を指定する。

例

app/api/hello/route.ts に書くと、/api/hello へのリクエストを処理する。

export async function GET() {
  return new NextResponse("Hello, Next.js!")
}

Client Component からは、エンドポイントを呼ぶだけでよい。

"use client"
 
export function LikeButton({ photoId }: { photoId: string }) {
  return (
    <button
      onClick={() => {
        fetch(`/api/photos/${photoId}/like`, { method: "POST" })
      }}
    >
      いいね
    </button>
  )
}
export async function POST(_: Request, { params }: { params: { photoId: string } }) {
  console.log(`photoId: ${params.photoId} が「いいね」されました`)
  return Response.json({ liked: true })
}

注意点

同じ階層に page.tsx も置くと、衝突してエラーになる。

自分の理解

Static になるかどうかに HTTP メソッドは関係ないようだった。Dynamic になる条件は日本語訳にはあったが原文から消えていた。Next.js 15 で Dynamic がデフォルトになったためだと思う。"next": "14.2.5" で Dynamic に切り替わることは確認した。

関連

出典