> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/vercel/next.js/llms.txt
> Use this file to discover all available pages before exploring further.

# route.js

> API reference for the route.js file convention. Create custom HTTP request handlers using the Web Request and Response APIs.

Route Handlers let you create custom HTTP endpoints for a route using the Web [Request](https://developer.mozilla.org/docs/Web/API/Request) and [Response](https://developer.mozilla.org/docs/Web/API/Response) APIs.

```ts route.ts theme={null}
export async function GET() {
  return Response.json({ message: 'Hello World' })
}
```

## HTTP methods

The following HTTP methods are supported: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, and `OPTIONS`.

```ts route.ts theme={null}
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) {}
export async function OPTIONS(request: Request) {}
```

<Note>If `OPTIONS` is not defined, Next.js automatically implements it and sets the `Allow` response header based on the other defined methods.</Note>

## Parameters

<ParamField path="request" type="NextRequest">
  A [NextRequest](/api-reference/functions/next-request) object, which extends the Web `Request` API with convenience methods for cookies and a parsed `nextUrl` object.

  ```ts route.ts theme={null}
  import type { NextRequest } from 'next/server'

  export async function GET(request: NextRequest) {
    const url = request.nextUrl
  }
  ```
</ParamField>

<ParamField path="context" type="object">
  An optional second parameter containing route context.

  **`context.params`** — a promise that resolves to the dynamic route parameters for the current route.

  ```ts app/dashboard/[team]/route.ts theme={null}
  export async function GET(
    request: Request,
    { params }: { params: Promise<{ team: string }> }
  ) {
    const { team } = await params
  }
  ```

  | Route                            | 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'] }>`    |
</ParamField>

## TypeScript helper

Use the global `RouteContext` helper for strongly typed route params:

```ts app/users/[id]/route.ts theme={null}
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 })
}
```

<Note>Types are generated during `next dev`, `next build`, or `next typegen`. `RouteContext` is globally available and does not need to be imported.</Note>

## Examples

### Reading and setting cookies

```ts route.ts theme={null}
import { cookies } from 'next/headers'

export async function GET(request: Request) {
  const cookieStore = await cookies()
  const token = cookieStore.get('token')

  return new Response('Hello', {
    headers: { 'Set-Cookie': `token=${token?.value}` },
  })
}
```

### Reading request headers

```ts route.ts theme={null}
import { headers } from 'next/headers'

export async function GET(request: Request) {
  const headersList = await headers()
  const referer = headersList.get('referer')

  return new Response('Hello', {
    headers: { referer: referer ?? '' },
  })
}
```

### Reading the request body

```ts app/items/route.ts theme={null}
export async function POST(request: Request) {
  const body = await request.json()
  return Response.json({ received: body })
}
```

### Reading form data

```ts app/items/route.ts theme={null}
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 })
}
```

### URL query parameters

```ts app/api/search/route.ts theme={null}
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
}
```

### Dynamic route segments

```ts app/items/[slug]/route.ts theme={null}
export async function GET(
  request: Request,
  { params }: { params: Promise<{ slug: string }> }
) {
  const { slug } = await params
  return Response.json({ slug })
}
```

### Redirects

```ts app/api/route.ts theme={null}
import { redirect } from 'next/navigation'

export async function GET(request: Request) {
  redirect('https://nextjs.org/')
}
```

### Streaming responses

```ts app/api/route.ts theme={null}
export async function GET() {
  const encoder = new TextEncoder()

  const stream = new ReadableStream({
    async start(controller) {
      controller.enqueue(encoder.encode('<p>One</p>'))
      await new Promise(r => setTimeout(r, 200))
      controller.enqueue(encoder.encode('<p>Two</p>'))
      controller.close()
    },
  })

  return new Response(stream)
}
```

### CORS headers

```ts app/api/route.ts theme={null}
export async function GET(request: Request) {
  return new Response('Hello', {
    headers: {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
      'Access-Control-Allow-Headers': 'Content-Type, Authorization',
    },
  })
}
```

### Webhooks

```ts app/api/webhook/route.ts theme={null}
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 })
}
```

### Non-UI responses

```ts app/rss.xml/route.ts theme={null}
export async function GET() {
  return new Response(
    `<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>My Site</title>
    <link>https://example.com</link>
  </channel>
</rss>`,
    { headers: { 'Content-Type': 'text/xml' } }
  )
}
```

### Revalidating cached data

```ts app/posts/route.ts theme={null}
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)
}
```

## Version history

| Version      | Changes                                                           |
| ------------ | ----------------------------------------------------------------- |
| `v15.0.0-RC` | `context.params` is now a promise                                 |
| `v15.0.0-RC` | Default caching for `GET` handlers changed from static to dynamic |
| `v13.2.0`    | Route Handlers introduced                                         |
