How to Use API Routes in Next.js for Backend Functionality
Building a modern web app often means juggling two codebases: one for the UI and another for the server. Next.js collapses that split by letting you write backend logic right inside your project. Depending on whether you’re using the Pages Router or the newer App Router, you’ll reach for API Routes or Route Handlers. This post walks through both approaches, highlights real-world patterns, and shares best practices for 2025 and beyond.
1. Why put your backend in Next.js?
-
Backend-for-Frontend (BFF): create a thin API layer that lives next to the components that consume it — great for GraphQL wrappers, auth callbacks, or any logic that shouldn’t run in the browser. nextjs.org
-
Zero-config serverless: on Vercel (and most hosts) every API file is compiled to a serverless or edge function automatically.
-
Shared types & utilities: import the same validation schema or
lib/db.ts
from pages and server code alike.
2. API Routes in the Pages Router (/pages/api/*
)
2.1 Basic example
Every file in pages/api
maps to /api/*
and runs server-side only, never increasing the client bundle size. nextjs.org
2.2 Handling multiple HTTP verbs
export default function handler(req: NextApiRequest, res: NextApiResponse) {
switch (req.method) {
case 'GET':
return res.json({ posts: [] });
case 'POST':
// create a new post
return res.status(201).end();
default:
res.setHeader('Allow', ['GET', 'POST']);
return res.status(405).end(`Method ${req.method} Not Allowed`);
}
}
2.3 Dynamic routes & parameters
pages/api/posts/[id].ts
handles /api/posts/:id
. Inside, access req.query.id
.
2.4 Edge Runtime (optional)
Add an exported config block to move this route to the edge:
Edge routes start faster and sit closer to users, but lack full Node APIs. giovannibenussi.comnextjs.org
3. Route Handlers in the App Router (/app/api/route.ts
)
Since Next.js 13/14, the App Router ships with Route Handlers — a lighter, Web-standard way to build endpoints. They live in the app
directory and mirror the file-based routing you use for pages. nextjs.orgnextjs.org
3.1 Minimal handler
Export a function whose name matches the HTTP verb (GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD). The first parameter is a standard Request object; return a Response or NextResponse
.
3.2 Streaming & Edge by default
Route Handlers work in both Node and Edge runtimes and support streaming responses out of the box. You can opt-in to Edge explicitly:
Edge/Node parity and streaming support are part of what makes Route Handlers compelling. nextjs.orgrunebook.dev
3.3 Nested & dynamic segments
Place a route.ts
in any nested folder
3.4 Working with Server Actions
Because both Server Actions and Route Handlers live on the server, you can call one from the other (or retire simple handlers in favor of directly using actions). stackoverflow.com
4. Choosing between the two
When to use | Pages-Router API Routes | App-Router Route Handlers |
---|---|---|
Codebase uses Pages Router only | ✔️ | (Not available) |
Migrating to App Router gradually | Keep existing API Routes | Write new endpoints in app/api |
Need edge-optimized streaming, static regeneration for APIs | ⚠️ Experimental | ✔️ Built-in |
Prefer familiar Express-style req / res objects |
✔️ | Uses Fetch API |
If you start a brand-new Next.js 14 project, stick with Route Handlers. They’re lighter, Web-standard, and future-proof.
5. Best practices (works for both)
-
Validate inputs – plug in [Zod] or
joi
to parsereq.body
/await request.json()
. -
Type safety – co-locate shared TypeScript types (e.g.,
Post
) intypes.ts
and import on both sides. -
Auth & sessions – use NextAuth.js or your provider’s JWT library; remember handlers run server-side only.
-
Error handling – return consistent JSON
{error: '...'}
with appropriate status codes. -
Rate limiting & CORS – apply Middleware or libraries like
@vercel/edge-config
. -
Keep them small – if business logic grows, graduate to a dedicated service or tRPC / GraphQL layer. Lee Robinson’s February 2025 guide echoes this advice. nextjs.org
6. Deployment & environment variables
-
Vercel: each handler becomes a serverless or edge function automatically; logs appear in the dashboard.
-
Self-hosting / Docker: run
next start
, and Next.js spins up an HTTP server that executes your handlers. -
Env vars: create
.env.local
and access them in handlers withprocess.env.MY_SECRET
(API Routes) orprocess.env
/NextResponse.json()
(Route Handlers). The variables are never bundled to the client.
7. Conclusion
API Routes and Route Handlers let you blur the line between frontend and backend, speeding up full-stack development without spinning up a separate server. If you’re on the Pages Router, API Routes remain rock-solid. If you’re on the App Router — or starting fresh in 2025 — lean into Route Handlers for a thinner, faster layer that speaks the Fetch API and runs anywhere, even at the edge.
Happy building — and may your /api
always return 200 OK
! 🎉
Frequently Asked Questions (FAQ)
(Add this section at the end of your post to capture common doubts readers have after trying Next.js API Routes and Route Handlers.)
# | Question | Quick Answer |
---|---|---|
1 | What exactly is an API Route in Next.js? | Any file inside /pages/api automatically becomes a server-side endpoint (e.g. pages/api/hello.ts → /api/hello ). It’s bundled for the server only, so it never bloats the client bundle. nextjs.org |
2 | How is a Route Handler different? | Route Handlers live in the App Router (/app/**/route.ts ) and use the standard Fetch Request/Response APIs. They’re the spiritual successor to API Routes, work in Node or Edge by default, and integrate tightly with Server Components and Server Actions. nextjs.orgnextjs.org |
3 | Can I keep API Routes and gradually adopt Route Handlers? | Yes. Pages-Router code can stay in pages/api while new endpoints are created in app/api . The App Router takes priority, so make sure paths don’t collide. nextjs.org |
4 | Do these endpoints run on the Edge? | Both support the Edge Runtime. For API Routes add export const config = { runtime: 'edge' } ; for Route Handlers add export const runtime = 'edge' . Note that some Node APIs (e.g., fs ) aren’t available at the Edge. nextjs.orgnextjs.org |
5 | How do I read the request body and query params? | API Routes use req.body , req.query . Route Handlers use the Web APIs: await request.json() for the body and new URL(request.url).searchParams for queries. nextjs.orgnextjs.org |
6 | What’s the easiest way to validate input? | Drop in a schema validator (e.g., Zod, Joi) inside the handler before using the data. Because these files run server-side only, the extra dependency won’t affect client bundles. (No special Next.js API needed.) |
7 | Do I need to set up CORS? | Same-origin calls from your own frontend just work. For third-party callers add CORS headers manually or via a small middleware; Next.js doesn’t inject them automatically. nextjs.org |
8 | How do I secure endpoints with authentication? | Because the code runs server-side, you can: 1) read cookies/session tokens, 2) verify JWTs, or 3) plug in NextAuth.js. For Route Handlers you can also share auth logic through Middleware for all /api/* paths. |
9 | Where do I put my environment variables? | Create .env.local , then access them in handlers with process.env.MY_SECRET . Values are never shipped to the client unless they start with NEXT_PUBLIC_ . nextjs.org |
10 | Will this scale, or should I spin up a separate backend? | Inline APIs are perfect for BFF layers, small CRUD helpers, and auth callbacks. When business logic or traffic grows (e.g., heavy data processing, long-running jobs), off-load to a dedicated service while the Next.js layer stays a thin adaptor. nextjs.org |
11 | Can I write these handlers in TypeScript? | Absolutely. Import NextApiRequest/NextApiResponse for API Routes or use the built-in Request/Response types for Route Handlers. Next.js will transpile them automatically. nextjs.org |
12 | How do I test my API endpoints locally? | Run next dev and hit http://localhost:3000/api/... with your browser, Postman, or curl. Unit-test logic separately and use supertest or undici for integration tests. |
13 | What’s the deployment story? | On Vercel each file becomes an isolated serverless or Edge Function with logs in the dashboard. In Docker/self-host mode, next start spins up an express-style server that routes requests to your handlers. |
14 | Can I stream responses (SSE, fetch streaming)? | Yes, Route Handlers natively support the Fetch streaming APIs. For API Routes you can use Node streams, but they don’t run on the Edge. nextjs.org |
15 | How do dynamic segments work? | Put [id].ts (Pages Router) or [id]/route.ts (App Router) and read req.query.id or parse from request.url . nextjs.orgnextjs.org |