docs/01-app/01-getting-started/15-route-handlers-and-middleware.mdx
Route Handlers allow you to create custom request handlers for a given route using the Web Request and 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
appdirectory. They are the equivalent of API Routes inside thepagesdirectory meaning you do not need to use API Routes and Route Handlers together.
Route Handlers are defined in a route.js|ts file inside the app directory:
export async function GET(request: Request) {}
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.
The following 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.
NextRequest and NextResponse APIsIn addition to supporting the native Request and Response APIs, Next.js extends them with NextRequest and NextResponse to provide convenient helpers for advanced use cases.
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 such as export const dynamic = 'force-static' in your Route Handler file.
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 })
}
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
GETmethod that is cached, in the same file.
Special Route Handlers like sitemap.ts, opengraph-image.tsx, and icon.tsx, and other metadata files remain static by default unless they use Dynamic APIs or dynamic config options.
You can consider a route the lowest level routing primitive.
page.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.
export default function Page() {
return <h1>Hello, Next.js!</h1>
}
// Conflict
// `app/route.ts`
export async function POST(request: Request) {}
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, or explore the Route Handlers API Reference.
In TypeScript, you can type the context parameter for Route Handlers with the globally available RouteContext 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.
Middleware allows you to run code before a request is completed. Then, based on the incoming request, you can modify the response by rewriting, redirecting, modifying the request or response headers, or responding directly.
Some common scenarios where Middleware is effective include:
Middleware is not a good fit for:
Using fetch with options.cache, options.next.revalidate, or options.next.tags, has no effect in Middleware.
Create a middleware.ts (or .js) file in the project root, or inside src if applicable, so that it is located at the same level as pages or app.
Note: While only one
middleware.tsfile is supported per project, you can still organize your middleware logic into modules. Break out middleware functionalities into separate.tsor.jsfiles and import them into your mainmiddleware.tsfile. This allows for cleaner management of route-specific middleware, aggregated in themiddleware.tsfor centralized control. By enforcing a single middleware file, it simplifies configuration, prevents potential conflicts, and optimizes performance by avoiding multiple middleware layers.
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
// This function can be marked `async` if using `await` inside
export function middleware(request: NextRequest) {
return NextResponse.redirect(new URL('/home', request.url))
}
// See "Matching Paths" below to learn more
export const config = {
matcher: '/about/:path*',
}
import { NextResponse } from 'next/server'
// This function can be marked `async` if using `await` inside
export function middleware(request) {
return NextResponse.redirect(new URL('/home', request.url))
}
// See "Matching Paths" below to learn more
export const config = {
matcher: '/about/:path*',
}
Read more about using middleware, or refer to the middleware API reference.