route.js
Source URL: https://nextjs.org/docs/app/api-reference/file-conventions/route
route.js
Section titled “route.js”Route Handlers allow you to create custom request handlers for a given route using the Web Request and Response APIs.
export async function GET() { return Response.json({ message: 'Hello World' })}export async function GET() { return Response.json({ message: 'Hello World' })}Reference
Section titled “Reference”HTTP Methods
Section titled “HTTP Methods”A route file allows you to create custom request handlers for a given route. The following HTTP methods are supported: GET, POST, PUT, PATCH, DELETE, HEAD, and OPTIONS.
export async function GET(request: Request) {}
export async function HEAD(request: Request) {}
export async function POST(request: Request) {}
export async function PUT(request: Request) {}
export async function DELETE(request: Request) {}
export async function PATCH(request: Request) {}
// If `OPTIONS` is not defined, Next.js will automatically implement `OPTIONS` and set the appropriate Response `Allow` header depending on the other methods defined in the Route Handler.export async function OPTIONS(request: Request) {}export async function GET(request) {}
export async function HEAD(request) {}
export async function POST(request) {}
export async function PUT(request) {}
export async function DELETE(request) {}
export async function PATCH(request) {}
// If `OPTIONS` is not defined, Next.js will automatically implement `OPTIONS` and set the appropriate Response `Allow` header depending on the other methods defined in the Route Handler.export async function OPTIONS(request) {}Parameters
Section titled “Parameters”request (optional)
Section titled “request (optional)”The request object is a NextRequest object, which is an extension of the Web Request API. NextRequest gives you further control over the incoming request, including easily accessing cookies and an extended, parsed, URL object nextUrl.
import type { NextRequest } from 'next/server'
export async function GET(request: NextRequest) { const url = request.nextUrl}export async function GET(request) { const url = request.nextUrl}context (optional)
Section titled “context (optional)”params: a promise that resolves to an object containing the dynamic route parameters for the current route.
export async function GET( request: Request, { params }: { params: Promise<{ team: string }> }) { const { team } = await params}export async function GET(request, { params }) { const { team } = await params}| Example | URL | params |
|---|---|---|
app/dashboard/[team]/route.js | /dashboard/1 | Promise<{ team: '1' }> |
app/shop/[tag]/[item]/route.js | /shop/1/2 | Promise<{ tag: '1', item: '2' }> |
app/blog/[...slug]/route.js | /blog/1/2 | Promise<{ slug: ['1', '2'] }> |
Route Context Helper
Section titled “Route Context Helper”You can type the Route Handler context using RouteContext to get strongly typed params from a route literal. RouteContext is a globally available helper.
import type { NextRequest } from 'next/server'
export async function GET(_req: NextRequest, ctx: RouteContext<'/users/[id]'>) { const { id } = await ctx.params return Response.json({ id })}Good to know
- Types are generated during
next dev,next buildornext typegen.- After type generation, the
RouteContexthelper is globally available. It doesn’t need to be imported.
Examples
Section titled “Examples”Cookies
Section titled “Cookies”You can read or set cookies with cookies from next/headers.
import { cookies } from 'next/headers'
export async function GET(request: NextRequest) { const cookieStore = await cookies()
const a = cookieStore.get('a') const b = cookieStore.set('b', '1') const c = cookieStore.delete('c')}import { cookies } from 'next/headers'
export async function GET(request) { const cookieStore = await cookies()
const a = cookieStore.get('a') const b = cookieStore.set('b', '1') const c = cookieStore.delete('c')}Alternatively, you can return a new Response using the Set-Cookie header.
import { cookies } from 'next/headers'
export async function GET(request: Request) { const cookieStore = await cookies() const token = cookieStore.get('token')
return new Response('Hello, Next.js!', { status: 200, headers: { 'Set-Cookie': `token=${token.value}` }, })}import { cookies } from 'next/headers'
export async function GET(request) { const cookieStore = await cookies() const token = cookieStore.get('token')
return new Response('Hello, Next.js!', { status: 200, headers: { 'Set-Cookie': `token=${token.value}` }, })}You can also use the underlying Web APIs to read cookies from the request (NextRequest):
import { type NextRequest } from 'next/server'
export async function GET(request: NextRequest) { const token = request.cookies.get('token')}export async function GET(request) { const token = request.cookies.get('token')}Headers
Section titled “Headers”You can read headers with headers from next/headers.
import { headers } from 'next/headers'import type { NextRequest } from 'next/server'
export async function GET(request: NextRequest) { const headersList = await headers() const referer = headersList.get('referer')}import { headers } from 'next/headers'
export async function GET(request) { const headersList = await headers() const referer = headersList.get('referer')}This headers instance is read-only. To set headers, you need to return a new Response with new headers.
import { headers } from 'next/headers'
export async function GET(request: Request) { const headersList = await headers() const referer = headersList.get('referer')
return new Response('Hello, Next.js!', { status: 200, headers: { referer: referer }, })}import { headers } from 'next/headers'
export async function GET(request) { const headersList = await headers() const referer = headersList.get('referer')
return new Response('Hello, Next.js!', { status: 200, headers: { referer: referer }, })}You can also use the underlying Web APIs to read headers from the request (NextRequest):
import { type NextRequest } from 'next/server'
export async function GET(request: NextRequest) { const requestHeaders = new Headers(request.headers)}export async function GET(request) { const requestHeaders = new Headers(request.headers)}Revalidating Cached Data
Section titled “Revalidating Cached Data”You can revalidate cached data using the revalidate route segment config option.
export const revalidate = 60
export async function GET() { const data = await fetch('https://api.vercel.app/blog') const posts = await data.json()
return Response.json(posts)}export const revalidate = 60
export async function GET() { const data = await fetch('https://api.vercel.app/blog') const posts = await data.json()
return Response.json(posts)}Redirects
Section titled “Redirects”import { redirect } from 'next/navigation'
export async function GET(request: Request) { redirect('https://nextjs.org/')}import { redirect } from 'next/navigation'
export async function GET(request) { redirect('https://nextjs.org/')}Dynamic Route Segments
Section titled “Dynamic Route Segments”Route Handlers can use Dynamic Segments to create request handlers from dynamic data.
export async function GET( request: Request, { params }: { params: Promise<{ slug: string }> }) { const { slug } = await params // 'a', 'b', or 'c'}export async function GET(request, { params }) { const { slug } = await params // 'a', 'b', or 'c'}| Route | Example URL | params |
|---|---|---|
app/items/[slug]/route.js | /items/a | Promise<{ slug: 'a' }> |
app/items/[slug]/route.js | /items/b | Promise<{ slug: 'b' }> |
app/items/[slug]/route.js | /items/c | Promise<{ slug: 'c' }> |
Static Generation with generateStaticParams
Section titled “Static Generation with generateStaticParams”You can use generateStaticParams with dynamic Route Handlers to statically generate responses at build time for specified params, while handling other params dynamically at request time.
When using Cache Components, you can combine generateStaticParams with use cache to enable data caching for both prerendered and runtime params.
See the generateStaticParams with Route Handlers documentation for examples and details.
URL Query Parameters
Section titled “URL Query Parameters”The request object passed to the Route Handler is a NextRequest instance, which includes some additional convenience methods, such as those for more easily handling query parameters.
import { type NextRequest } from 'next/server'
export function GET(request: NextRequest) { const searchParams = request.nextUrl.searchParams const query = searchParams.get('query') // query is "hello" for /api/search?query=hello}export function GET(request) { const searchParams = request.nextUrl.searchParams const query = searchParams.get('query') // query is "hello" for /api/search?query=hello}Streaming
Section titled “Streaming”Streaming is commonly used in combination with Large Language Models (LLMs), such as OpenAI, for AI-generated content. Learn more about the AI SDK.
import { openai } from '@ai-sdk/openai'import { StreamingTextResponse, streamText } from 'ai'
export async function POST(req: Request) { const { messages } = await req.json() const result = await streamText({ model: openai('gpt-4-turbo'), messages, })
return new StreamingTextResponse(result.toAIStream())}import { openai } from '@ai-sdk/openai'import { StreamingTextResponse, streamText } from 'ai'
export async function POST(req) { const { messages } = await req.json() const result = await streamText({ model: openai('gpt-4-turbo'), messages, })
return new StreamingTextResponse(result.toAIStream())}These abstractions use the Web APIs to create a stream. You can also use the underlying Web APIs directly.
// https://developer.mozilla.org/docs/Web/API/ReadableStream#convert_async_iterator_to_streamfunction iteratorToStream(iterator: any) { return new ReadableStream({ async pull(controller) { const { value, done } = await iterator.next()
if (done) { controller.close() } else { controller.enqueue(value) } }, })}
function sleep(time: number) { return new Promise((resolve) => { setTimeout(resolve, time) })}
const encoder = new TextEncoder()
async function* makeIterator() { yield encoder.encode('<p>One</p>') await sleep(200) yield encoder.encode('<p>Two</p>') await sleep(200) yield encoder.encode('<p>Three</p>')}
export async function GET() { const iterator = makeIterator() const stream = iteratorToStream(iterator)
return new Response(stream)}// https://developer.mozilla.org/docs/Web/API/ReadableStream#convert_async_iterator_to_streamfunction iteratorToStream(iterator) { return new ReadableStream({ async pull(controller) { const { value, done } = await iterator.next()
if (done) { controller.close() } else { controller.enqueue(value) } }, })}
function sleep(time) { return new Promise((resolve) => { setTimeout(resolve, time) })}
const encoder = new TextEncoder()
async function* makeIterator() { yield encoder.encode('<p>One</p>') await sleep(200) yield encoder.encode('<p>Two</p>') await sleep(200) yield encoder.encode('<p>Three</p>')}
export async function GET() { const iterator = makeIterator() const stream = iteratorToStream(iterator)
return new Response(stream)}Request Body
Section titled “Request Body”You can read the Request body using the standard Web API methods:
export async function POST(request: Request) { const res = await request.json() return Response.json({ res })}export async function POST(request) { const res = await request.json() return Response.json({ res })}Request Body FormData
Section titled “Request Body FormData”You can read the FormData using the request.formData() function:
export async function POST(request: Request) { const formData = await request.formData() const name = formData.get('name') const email = formData.get('email') return Response.json({ name, email })}export async function POST(request) { const formData = await request.formData() const name = formData.get('name') const email = formData.get('email') return Response.json({ name, email })}Since formData data are all strings, you may want to use zod-form-data to validate the request and retrieve data in the format you prefer (e.g. number).
You can set CORS headers for a specific Route Handler using the standard Web API methods:
export async function GET(request: Request) { return new Response('Hello, Next.js!', { status: 200, headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type, Authorization', }, })}export async function GET(request) { return new Response('Hello, Next.js!', { status: 200, headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type, Authorization', }, })}Good to know:
- To add CORS headers to multiple Route Handlers, you can use Proxy or the
next.config.jsfile.
Webhooks
Section titled “Webhooks”You can use a Route Handler to receive webhooks from third-party services:
export async function POST(request: Request) { try { const text = await request.text() // Process the webhook payload } catch (error) { return new Response(`Webhook error: ${error.message}`, { status: 400, }) }
return new Response('Success!', { status: 200, })}export async function POST(request) { try { const text = await request.text() // Process the webhook payload } catch (error) { return new Response(`Webhook error: ${error.message}`, { status: 400, }) }
return new Response('Success!', { status: 200, })}Notably, unlike API Routes with the Pages Router, you do not need to use bodyParser to use any additional configuration.
Non-UI Responses
Section titled “Non-UI Responses”You can use Route Handlers to return non-UI content. Note that sitemap.xml, robots.txt, app icons, and open graph images all have built-in support.
export async function GET() { return new Response( `<?xml version="1.0" encoding="UTF-8" ?><rss version="2.0">
<channel> <title>Next.js Documentation</title> <link>https://nextjs.org/docs</link> <description>The React Framework for the Web</description></channel>
</rss>`, { headers: { 'Content-Type': 'text/xml', }, } )}export async function GET() { return new Response(`<?xml version="1.0" encoding="UTF-8" ?><rss version="2.0">
<channel> <title>Next.js Documentation</title> <link>https://nextjs.org/docs</link> <description>The React Framework for the Web</description></channel>
</rss>`)}Segment Config Options
Section titled “Segment Config Options”Route Handlers use the same route segment configuration as pages and layouts.
export const dynamic = 'auto'export const dynamicParams = trueexport const revalidate = falseexport const fetchCache = 'auto'export const runtime = 'nodejs'export const preferredRegion = 'auto'export const dynamic = 'auto'export const dynamicParams = trueexport const revalidate = falseexport const fetchCache = 'auto'export const runtime = 'nodejs'export const preferredRegion = 'auto'See the API reference for more details.
Version History
Section titled “Version History”| Version | Changes |
|---|---|
v15.0.0-RC | context.params is now a promise. A codemod is available |
v15.0.0-RC | The default caching for GET handlers was changed from static to dynamic |
v13.2.0 | Route Handlers are introduced. |