How to Build a REST API with Next.js App Router in 2026 - The Right Way

How to Build a REST API with Next.js App Router in 2026 - The Right Way

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/:id

Each 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 zod

ts

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.

O

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.