Next.js enables you to define Route Handlers in the app/api/
directory. These handlers run on the server and allow you to implement backend logic such as RESTful APIs, database access, webhooks, and cross-platform communication (e.g. mobile apps).
In the App Router, prefer using Server Components for server-side logic. Use Route Handlers only when a public HTTP endpoint is required.
Pros: Accessible from any frontend; aligns with traditional API design
Cons: API paths (/api/...
) are exposed to the client, requiring authentication and CORS handling
Each Route Handler lives in its own folder under app/api/
, with logic placed in a route.ts
file. Supports all HTTP methods: GET
, POST
, PUT
, DELETE
, PATCH
, OPTIONS
, HEAD
Basic Example
// app/api/hello/route.ts
export async function GET() {
return new Response(JSON.stringify({ message: "Hello World" }), {
headers: { "Content-Type": "application/json" },
});
}
export async function POST(req: Request) {
const body = await req.json();
return new Response(JSON.stringify({ received: body }), {
headers: { "Content-Type": "application/json" },
});
}
Use fetch
inside client or server components
const fetchData = async () => {
const res = await fetch("/api/hello");
const data = await res.json();
console.log(data);
};
const sendData = async () => {
const res = await fetch("/api/user", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "Alice" }),
});
const data = await res.json();
console.log(data);
};
Protect handlers using headers, cookies, or tokens
// app/api/protected/route.ts
import { NextRequest, NextResponse } from "next/server";
export async function GET(req: NextRequest) {
const token = req.headers.get("Authorization");
if (token !== "Bearer mysecrettoken") {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
return NextResponse.json({ message: "Authenticated" });
}
Connect to any database (PostgreSQL, MySQL, MongoDB, etc.)