
Next.js App Router changed how we build APIs. In this guide, you'll learn the right way to build REST APIs in 2026 - with real code, folder structure, and production tips.
If you've been building with Next.js, you've probably heard the words "App Router" a lot lately. But one question beginners always ask is: can I build a REST API with Next.js App Router? And more importantly - what's the right way to do it in 2026?
In this tutorial, you'll learn exactly that. By the end, you'll have a working REST API using Next.js Route Handlers - with clean folder structure, TypeScript, basic validation, and tips for production use.
What Changed with App Router?
Before Next.js 13, you'd create API routes inside the pages/api/ folder. Those still work, but the App Router introduced a better way: Route Handlers.
Route Handlers live in the app/ directory alongside your pages. This keeps your API logic close to your UI logic - and they support all HTTP methods: GET, POST, PUT, DELETE, and more.
Step 1 - Folder Structure
Here's the clean structure you should follow:
app/
api/
users/
route.ts ← handles GET /api/users and POST /api/users
users/
[id]/
route.ts ← handles GET /api/users/:id and DELETE /api/users/:idEach route.ts file is your endpoint. Simple and clean.
Step 2 - Create Your First GET Endpoint
Inside app/api/users/route.ts:
ts
import { NextResponse } from 'next/server'
export async function GET() {
const users = [
{ id: 1, name: 'Usama', role: 'developer' },
{ id: 2, name: 'Ali', role: 'designer' },
]
return NextResponse.json(users, { status: 200 })
}Now visit http://localhost:3000/api/users - you'll see the JSON response.
Step 3 - Handle POST with a Request Body
ts
import { NextRequest, NextResponse } from 'next/server'
export async function POST(req: NextRequest) {
const body = await req.json()
if (!body.name) {
return NextResponse.json(
{ error: 'Name is required' },
{ status: 400 }
)
}
const newUser = { id: Date.now(), name: body.name }
return NextResponse.json(newUser, { status: 201 })
}Key things here: always parse req.json(), always return proper status codes, and always validate the body before processing.
Step 4 - Dynamic Routes (GET by ID)
Inside app/api/users/[id]/route.ts:
ts
import { NextRequest, NextResponse } from 'next/server'
export async function GET(
req: NextRequest,
{ params }: { params: { id: string } }
) {
const { id } = params
// In real apps, fetch this from your DB (PostgreSQL, MongoDB, etc.)
const user = { id, name: 'Usama', role: 'developer' }
if (!user) {
return NextResponse.json({ error: 'User not found' }, { status: 404 })
}
return NextResponse.json(user)
}Step 5 - Add Basic Validation with Zod
Don't skip validation - it's what separates a real API from a toy project.
bash
npm install zodts
import { z } from 'zod'
import { NextRequest, NextResponse } from 'next/server'
const UserSchema = z.object({
name: z.string().min(2),
email: z.string().email(),
})
export async function POST(req: NextRequest) {
const body = await req.json()
const result = UserSchema.safeParse(body)
if (!result.success) {
return NextResponse.json(
{ errors: result.error.flatten() },
{ status: 422 }
)
}
// Safe to use result.data now
return NextResponse.json({ created: true, data: result.data }, { status: 201 })
}Step 6 - CORS and Headers (Production Must-Do)
ts
import { NextResponse } from 'next/server'
export async function GET() {
const response = NextResponse.json({ message: 'Hello from API' })
response.headers.set('Access-Control-Allow-Origin', '*')
response.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE')
return response
}For production, replace * with your actual frontend domain.
Common Mistakes to Avoid
Don't use pages/api/ and app/api/ together - pick one and stick to it
Always return a NextResponse - returning plain objects won't work in App Router
Don't skip status codes - 200, 201, 400, 404, 500 all matter
Always handle errors - wrap your logic in try/catch for production apps
What's Next?
Now that you can build a basic REST API with Next.js App Router, here's what to learn next:
Connect it to a real database using Prisma + PostgreSQL
Add authentication using NextAuth.js or Clerk
Protect routes using Next.js Middleware
Deploy your full-stack app on Vercel
Final Thoughts
Next.js App Router makes building REST APIs cleaner and more organized than ever. The Route Handlers pattern keeps your backend and frontend in one project - which is perfect for solo developers and small teams.
If you found this useful, share it with someone learning Next.js. And if you have questions, drop them in the comments - I read every one.
Osama Habib
Multan, Pakistan
Full Stack Developer specialising in Next.js, Node.js, and the MERN stack. I write about modern web development, system design, and practical engineering.
