Loading...
Loading...
Route Handlers (called API Routes in the Pages Router) let you create server-side API endpoints directly in a Next.js application. They are defined in route.ts files inside the app directory.
// app/api/problems/route.ts
import { NextResponse } from 'next/server'
import { db } from '@/lib/db'
export async function GET() {
const problems = await db.problem.findMany()
return NextResponse.json(problems)
}
export async function POST(request: Request) {
const body = await request.json()
const problem = await db.problem.create({
data: body
})
return NextResponse.json(problem, { status: 201 })
}
Each exported method (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS) handles the corresponding HTTP request.
// app/api/problems/[id]/route.ts
import { NextResponse } from 'next/server'
import { db } from '@/lib/db'
export async function GET(
request: Request,
{ params }: { params: { id: string } }
) {
const problem = await db.problem.findUnique({
where: { id: params.id }
})
if (!problem) {
return NextResponse.json(
{ error: 'Problem not found' },
{ status: 404 }
)
}
return NextResponse.json(problem)
}
export async function DELETE(
request: Request,
{ params }: { params: { id: string } }
) {
await db.problem.delete({ where: { id: params.id } })
return new Response(null, { status: 204 })
}
Route Handlers use the standard Web API Request:
// app/api/search/route.ts
export async function GET(request: Request) {
const { searchParams } = new URL(request.url)
const query = searchParams.get('q')
if (!query) {
return NextResponse.json(
{ error: 'Query parameter required' },
{ status: 400 }
)
}
const results = await db.problem.findMany({
where: { name: { contains: query, mode: 'insensitive' } }
})
return NextResponse.json(results)
}
import { cookies, headers } from 'next/headers'
export async function GET() {
const cookieStore = cookies()
const token = cookieStore.get('session')
const headersList = headers()
const userAgent = headersList.get('user-agent')
return NextResponse.json({ token, userAgent })
}
GET requests that do not read the Request are cached by default:
// Cached (static Route Handler)
export async function GET() {
const data = await fetch('https://api.example.com/data')
return NextResponse.json(data)
}
// Not cached (reads Request)
export async function GET(request: Request) {
const { searchParams } = new URL(request.url)
// ...
}
For explicit cache control:
export const dynamic = 'force-dynamic' // always dynamic
export const revalidate = 300 // revalidate after 5 minutes
| Route Handlers | Server Actions | |
|---|---|---|
| Purpose | API endpoints | UI mutations |
| HTTP methods | GET, POST, PUT, DELETE... | POST only |
| Called from | Any client (fetch, curl) | Components only |
| Progressive enhancement | No | Yes (forms) |
When to use which:
Use Server Actions for data mutations from the interface (forms, buttons). Use Route Handlers for public APIs, webhooks, integrations with external services and cases where specific HTTP methods are needed.