Route Handlers and Middleware
Route Handlers
Route Handlers allow you to create custom request handlers for a given route using the Web Requestand ResponseAPIs.

Good to know: Route Handlers are only available inside the
app
directory. They are the equivalent of API Routes inside thepages
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 inside the app
directory:
export async function GET(request: 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 methodsare supported: GET
, POST
, PUT
, ,
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 Requestand ResponseAPIs, Next.js extends them with NextRequest
and NextResponse
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 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 })
}
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.
Special Route Handlers
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.
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 aspage.js
.
Page | Route | Result |
---|---|---|
app/page.js | app/route.js | Conflict |
app/page.js | app/api/route.js | Valid |
app/[user]/page.js | app/api/route.js | 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) {}
Read more about how Route Handlers complement your frontend application, or explore the Route Handlers API Reference.
Middleware
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.
Use cases
Some common scenarios where Middleware is effective include:
- Quick redirects after reading parts of the incoming request
- Rewriting to different pages based on A/B tests or experiments
- Modifying headers for all pages or a subset of pages
Middleware is not a good fit for:
- Slow data fetching
- Session management
Using fetch with options.cache
, options.next.revalidate
, or options.next.tags
, has no effect in Middleware.
Convention
Use the file middleware.ts
(or .js
) in the root of your project to define Middleware. For example, at the same level as pages
or app
, or inside src
if applicable.
Note: While only one
middleware.ts
file is supported per project, you can still organize your middleware logic into modules. Break out middleware functionalities into separate.ts
or.js
files and import them into your mainmiddleware.ts
file. This allows for cleaner management of route-specific middleware, aggregated in themiddleware.ts
for centralized control. By enforcing a single middleware file, it simplifies configuration, prevents potential conflicts, and optimizes performance by avoiding multiple middleware layers.
Example
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*',
}
Read more about using middleware
, or refer to the middleware
API reference.
API Reference
Was this helpful?