Files
vercel__next.js/docs/01-app/01-getting-started/15-route-handlers.mdx
Rich Haines e80a85bcff docs: improve discovery summaries (#97982)
This PR improves the meta descriptions on certain pages.

<!-- Thanks for opening a PR! Your contribution is much appreciated.
To make sure your PR is handled as smoothly as possible we request that
you follow the checklist sections below.
Choose the right checklist for the change(s) that you're making:

## For Contributors

We encourage you to use AI to assist you in researching, creating, and
reviewing changes. However, you must review and deeply understand the
contributions you are making. For this reason, **pull request
descriptions from external contributors must be written by a human**.

### Improving Documentation

- Run `pnpm prettier-fix` to fix formatting issues before opening the
PR.
- Read the Docs Contribution Guide to ensure your contribution follows
the docs guidelines:
https://nextjs.org/docs/community/contribution-guide

### Fixing a bug

- Related issues linked using `fixes #number`
- Tests added. See:
https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md

### Adding a feature

- Implements an existing feature request or RFC. Make sure the feature
request has been accepted for implementation before opening a PR. (A
discussion must be opened, see
https://github.com/vercel/next.js/discussions/new?category=ideas)
- Related issues/discussions are linked using `fixes #number`
- e2e tests added
(https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs)
- Documentation added
- Telemetry added. In case of a feature if it's used or not.
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md

### Signed commits

- This repository requires verified commit signatures on protected
branches.
- If this pull request is blocked for unsigned commits, re-sign the
commits and force-push the branch.
- A `Signed-off-by` line in the commit message is not enough.

## For Maintainers

- Minimal description (aim for explaining to someone not on the team to
understand the PR)
- When linking to a Slack thread, you might want to share details of the
conclusion
- Link both the Linear (Fixes NEXT-xxx) and the GitHub issues
- Add review comments if necessary to explain to the reviewer the logic
behind a change

### What?

### Why?

### How?

Closes NEXT-
Fixes #

-->
2026-08-27 19:47:39 +02:00

203 lines
8.1 KiB
Plaintext

---
title: Route Handlers
nav_title: Route Handlers
description: Create custom request handlers with Next.js Route Handlers using the Web Request and Response APIs.
related:
title: API Reference
description: Learn more about Route Handlers
links:
- app/api-reference/file-conventions/route
- app/guides/backend-for-frontend
---
## Route Handlers
Route Handlers allow you to create custom request handlers for a given route using the Web [Request](https://developer.mozilla.org/docs/Web/API/Request) and [Response](https://developer.mozilla.org/docs/Web/API/Response) APIs.
<Image
alt="Route.js Special File"
srcLight="/docs/light/route-special-file.png"
srcDark="/docs/dark/route-special-file.png"
width="1600"
height="444"
/>
> **Good to know**: Route Handlers are only available inside the `app` directory. They are the equivalent of [API Routes](/docs/pages/building-your-application/routing/api-routes) inside the `pages` directory meaning you **do not** need to use API Routes and Route Handlers together.
### Convention
Route Handlers are defined in a [`route.js|ts` file](/docs/app/api-reference/file-conventions/route) inside the `app` directory:
```ts filename="app/api/route.ts" switcher
export async function GET(request: Request) {}
```
```js filename="app/api/route.js" switcher
export async function GET(request) {}
```
Route Handlers can be nested anywhere inside the `app` directory, similar to `page.js` and `layout.js`. But there **cannot** be a `route.js` file at the same route segment level as `page.js`.
### Supported HTTP Methods
The following [HTTP methods](https://developer.mozilla.org/docs/Web/HTTP/Methods) are supported: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, and `OPTIONS`. If an unsupported method is called, Next.js will return a `405 Method Not Allowed` response.
### Extended `NextRequest` and `NextResponse` APIs
In addition to supporting the native [Request](https://developer.mozilla.org/docs/Web/API/Request) and [Response](https://developer.mozilla.org/docs/Web/API/Response) APIs, Next.js extends them with [`NextRequest`](/docs/app/api-reference/functions/next-request) and [`NextResponse`](/docs/app/api-reference/functions/next-response) to provide convenient helpers for advanced use cases.
### Caching
Route Handlers are not cached by default. You can, however, opt into caching for `GET` methods. Other supported HTTP methods are **not** cached. To cache a `GET` method, use a [route config option](/docs/app/guides/caching-without-cache-components#dynamic) such as `export const dynamic = 'force-static'` in your Route Handler file.
```ts filename="app/items/route.ts" switcher
export const dynamic = 'force-static'
export async function GET() {
const res = await fetch('https://data.mongodb-api.com/...', {
headers: {
'Content-Type': 'application/json',
'API-Key': process.env.DATA_API_KEY,
},
})
const data = await res.json()
return Response.json({ data })
}
```
```js filename="app/items/route.js" switcher
export const dynamic = 'force-static'
export async function GET() {
const res = await fetch('https://data.mongodb-api.com/...', {
headers: {
'Content-Type': 'application/json',
'API-Key': process.env.DATA_API_KEY,
},
})
const data = await res.json()
return Response.json({ data })
}
```
> **Good to know**: Other supported HTTP methods are **not** cached, even if they are placed alongside a `GET` method that is cached, in the same file.
#### With Cache Components
When [Cache Components](/docs/app/getting-started/caching) is enabled, `GET` Route Handlers follow the same model as normal UI routes in your application. They run at request time by default, can be prerendered when they don't access uncached or runtime data, and you can use `use cache` to include uncached data in the static response.
**Static example** - doesn't access uncached or runtime data, so it will be prerendered at build time:
```tsx filename="app/api/project-info/route.ts"
export async function GET() {
return Response.json({
projectName: 'Next.js',
})
}
```
**Dynamic example** - accesses non-deterministic operations. During the build, prerendering stops when `Math.random()` is called, deferring to request-time rendering:
```tsx filename="app/api/random-number/route.ts"
export async function GET() {
return Response.json({
randomNumber: Math.random(),
})
}
```
**Runtime data example** - accesses request-specific data. Prerendering terminates when runtime APIs like `headers()` are called:
```tsx filename="app/api/user-agent/route.ts"
import { headers } from 'next/headers'
export async function GET() {
const headersList = await headers()
const userAgent = headersList.get('user-agent')
return Response.json({ userAgent })
}
```
> **Good to know**: Prerendering stops if the `GET` handler accesses network requests, database queries, async file system operations, request object properties (like `req.url`, `request.headers`, `request.cookies`, `request.body`), runtime APIs like [`cookies()`](/docs/app/api-reference/functions/cookies), [`headers()`](/docs/app/api-reference/functions/headers), [`connection()`](/docs/app/api-reference/functions/connection), or non-deterministic operations.
**Cached example** - accesses uncached data (database query) but caches it with `use cache`, allowing it to be included in the prerendered response:
```tsx filename="app/api/products/route.ts"
import { cacheLife } from 'next/cache'
export async function GET() {
const products = await getProducts()
return Response.json(products)
}
async function getProducts() {
'use cache'
cacheLife('hours')
return await db.query('SELECT * FROM products')
}
```
> **Good to know**: `use cache` cannot be used directly inside a Route Handler body; extract it to a helper function. Cached responses revalidate according to `cacheLife` when a new request arrives.
### Special Route Handlers
Special Route Handlers like [`sitemap.ts`](/docs/app/api-reference/file-conventions/metadata/sitemap), [`opengraph-image.tsx`](/docs/app/api-reference/file-conventions/metadata/opengraph-image), and [`icon.tsx`](/docs/app/api-reference/file-conventions/metadata/app-icons), and other [metadata files](/docs/app/api-reference/file-conventions/metadata) remain static by default unless they use Request-time APIs or dynamic config options.
### Route Resolution
You can consider a `route` the lowest level routing primitive.
- They **do not** participate in layouts or client-side navigations like `page`.
- There **cannot** be a `route.js` file at the same route as `page.js`.
| Page | Route | Result |
| -------------------- | ------------------ | ---------------------------- |
| `app/page.js` | `app/route.js` | <Cross size={18} /> Conflict |
| `app/page.js` | `app/api/route.js` | <Check size={18} /> Valid |
| `app/[user]/page.js` | `app/api/route.js` | <Check size={18} /> Valid |
Each `route.js` or `page.js` file takes over all HTTP verbs for that route.
```ts filename="app/page.ts" switcher
export default function Page() {
return <h1>Hello, Next.js!</h1>
}
// Conflict
// `app/route.ts`
export async function POST(request: Request) {}
```
```js filename="app/page.js" switcher
export default function Page() {
return <h1>Hello, Next.js!</h1>
}
// Conflict
// `app/route.js`
export async function POST(request) {}
```
Read more about how Route Handlers [complement your frontend application](/docs/app/guides/backend-for-frontend), or explore the Route Handlers [API Reference](/docs/app/api-reference/file-conventions/route).
### Route Context Helper
In TypeScript, you can type the `context` parameter for Route Handlers with the globally available [`RouteContext`](/docs/app/api-reference/file-conventions/route#route-context-helper) helper:
```ts filename="app/users/[id]/route.ts" switcher
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 build` or `next typegen`.