Courses
Next.js
Route Handlers

Route Handlers

Route Handlers allow you to create custom request handlers for a given route using the Web Request and Response APIs.

Route Handlers are only available inside the app directory. They are the equivalent of 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 inside the app directory:

app/api/route.ts
export async function GET(request: Request) {}

Route Handlers can be nested 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 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 native Request and Response. Next.js extends them with NextRequest and NextResponse to provide convenient helpers for advanced use cases.

Behaviour

Static Route Handlers

Route Handlers are statically evaluated by default when using the GET method with the Response object.

app/items/route.ts
import { NextResponse } from "next/server";
 
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 NextResponse.json({ data });
}

TypeScript Warning: Although Response.json() is valid, native TypeScript types currently shows an error, you can use NextResponse.json() for typed responses instead.

Dynamic Route Handlers

Route handlers are evaluated dynamically when:

  • Using the Request object with the GET method.

  • Using any of the other HTTP methods.

  • Using Dynamic Functions like cookies and headers.

  • The Segment Config Options manually specifies dynamic mode.

For example:

app/products/api/route.ts
import { NextResponse } from "next/server";
 
export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const id = searchParams.get("id");
  const res = await fetch(`https://data.mongodb-api.com/product/${id}`, {
    headers: {
      "Content-Type": "application/json",
      "API-Key": process.env.DATA_API_KEY,
    },
  });
  const product = await res.json();
 
  return NextResponse.json({ product });
}

Similarly, the POST method will cause the Route Handler to be evaluated dynamically.

app/items/route.ts
import { NextResponse } from "next/server";
 
export async function POST() {
  const res = await fetch("https://data.mongodb-api.com/...", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "API-Key": process.env.DATA_API_KEY,
    },
    body: JSON.stringify({ time: new Date().toISOString() }),
  });
 
  const data = await res.json();
 
  return NextResponse.json(data);
}

Like API Routes, Route Handlers can be used for cases like handling form submissions. A new abstraction for handling forms and mutations that integrates deeply with React is being worked on.

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.

pageRouteResult
app/page.jsapp/route.js✘ Conflict
app/page.jsapp/api/route.js✓ Valid
Strikethroughapp/api/route.js✓ Valid

Each route.js or page.js file takes over all HTTP verbs for that route.

app/page.js
export default function Page() {
  return <h1>Hello, Next.js!</h1>
}
 
// ❌ Conflict
// `app/route.js`
export async function POST(request) {}