Free tutorials & notes in Hindi & English · Clean code examples 🧰 Free PDF & Image Tools →
🟣 Data, Server & APIs  ·  Lesson 21

Route Handlers (Build an API)

What is a Route Handler?

A Route Handler is a file named route.js that returns data (usually JSON) instead of a page. You export a function per HTTP method: GET, POST, PUT, PATCH, DELETE. It is how your Next.js site becomes an API for Android apps or other websites.

page.jsx

  • Returns HTML
  • For humans in a browser
  • URL: /courses

route.js

  • Returns JSON / text / file
  • For apps and scripts
  • URL: /api/courses
Page vs Route Handler

GET /api/courses

JS – app/api/courses/route.js
// app/api/courses/route.js  (from the EduSite project)
import { searchCourses } from '@/lib/courses'

// GET /api/courses?q=python  →  JSON list of matching courses.
export async function GET(request) {
  const q = request.nextUrl.searchParams.get('q') || ''
  const data = searchCourses(q).map(({ slug, title, level, fee, duration }) =>
    ({ slug, title, level, fee, duration }))
  return Response.json({ count: data.length, courses: data })
}

Query strings: ?q=python

request.nextUrl.searchParams.get('q') reads the query string. Test it:

Terminal
$ curl "http://localhost:3000/api/courses?q=python" {"count":1,"courses":[{"slug":"python-ai","title":"Python + AI","level":"Beginner","fee":3499,"duration":"8 weeks"}]}

POST with a JSON body

JS – app/api/enquiries/route.js
// app/api/enquiries/route.js
import { NextResponse } from 'next/server'

const enquiries = []   // demo only – use a database in real apps

export async function GET() {
  return NextResponse.json(enquiries)
}

export async function POST(request) {
  let body
  try {
    body = await request.json()
  } catch {
    return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 })
  }

  const name = String(body.name || '').trim()
  const phone = String(body.phone || '').trim()
  if (name.length < 2 || !/^[6-9]\d{9}$/.test(phone)) {
    return NextResponse.json({ error: 'Name and a valid 10-digit mobile are required' }, { status: 422 })
  }

  const item = { id: enquiries.length + 1, name, phone, at: new Date().toISOString() }
  enquiries.push(item)
  return NextResponse.json(item, { status: 201 })   // 201 = Created
}
Terminal – testing POST
$ curl -X POST http://localhost:3000/api/enquiries \ -H "Content-Type: application/json" \ -d '{"name":"Riya","phone":"9876543210"}' {"id":1,"name":"Riya","phone":"9876543210","at":"2026-10-19T09:12:44.120Z"} $ curl -X POST http://localhost:3000/api/enquiries -d '{"name":"R"}' {"error":"Name and a valid 10-digit mobile are required"} ← status 422

Dynamic API: /api/courses/[slug]

JS – app/api/courses/[slug]/route.js
// app/api/courses/[slug]/route.js
import { getCourse } from '@/lib/courses'

export async function GET(request, { params }) {
  const { slug } = await params              // params is a Promise here too
  const course = getCourse(slug)
  if (!course) {
    return Response.json({ error: 'Course not found' }, { status: 404 })
  }
  return Response.json(course)
}

Request → response flow

Android approute.jsDatabasePOST /api/enquiries {name, phone}validate inputINSERT enquirynew id201 Created {id: 1, ...}
A typical API call

Status codes and errors

CodeMeaningWhen to use
200OKSuccessful GET
201CreatedPOST created a record
400Bad RequestInvalid JSON / missing fields
401UnauthorizedNot logged in / bad token
404Not FoundRecord does not exist
422UnprocessableValidation failed
500Server ErrorUnexpected crash

Headers and CORS for mobile apps

Native Android apps (Retrofit) do not need CORS, but JavaScript on other websites does.

JS – CORS headers
// app/api/courses/route.js — allow any origin to read (public API)
const cors = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
  'Access-Control-Allow-Headers': 'Content-Type, Authorization',
}

export async function OPTIONS() {               // browser "preflight" request
  return new Response(null, { status: 204, headers: cors })
}

export async function GET(request) {
  const data = { ok: true }
  return Response.json(data, { headers: { ...cors, 'Cache-Control': 'public, max-age=60' } })
}

Testing your API

JS – calling the API
// From any JavaScript (browser console, another site, React Native):
const res = await fetch('https://your-site.com/api/courses?q=web')
const json = await res.json()
console.log(json.count, json.courses[0].title)

You can also open http://localhost:3000/api/courses in the browser, or use Postman / Thunder Client (VS Code extension) for POST requests.

Common mistakes

  • export default function in route.js → must be named exports (export async function GET).
  • Putting route.js next to page.jsx → conflict.
  • Returning 200 with { error } → apps cannot detect failure; set the right status.
  • Trusting input without validation → always validate on the server.

Summary

  • route.js exports GET, POST… and returns Response.json().
  • Read query via request.nextUrl.searchParams, body via await request.json(), path via await params.
  • Use correct status codes; add CORS only for cross-site browser clients.
✅ Practice

Create /api/courses/[slug] and /api/enquiries (GET + POST). Test POST with curl using a valid and an invalid phone number and check the status codes.

Frequently Asked Questions

Can page.jsx and route.js be in the same folder?
No. A folder is either a page or an API endpoint. Put APIs under app/api/... to keep them separate.
Do I need Route Handlers for my own forms?
Usually not. For forms inside your Next.js app, Server Actions are simpler. Route Handlers are for external clients such as Android apps, webhooks or other websites.
Are GET route handlers cached?
Not by default. A GET handler that reads the request is dynamic. You can opt into static output with export const dynamic = 'force-static'.
← Next.js Tutorial
🔗

Share this topic with a friend

यह topic किसी दोस्त को भेजें

Found it useful? Send it to a classmate learning the same thing.

अच्छा लगा? जो दोस्त यही सीख रहा है, उसे भेज दीजिए।

Next topicServer Actions & Forms Your progress: 21 / 37 (57%) · Lesson 22 / 37 Continue →

How was this lesson?

Your feedback helps us make the content better.

Write a review or suggestion (optional)

After approval, your name and city may appear publicly with your review. Please do not write your email or phone number.

▶ CodingEasily on YouTube

Learn with video lessons in easy Hindi

Featured video: RDBMS Part 3 – Class XI Commerce IT

▶ Subscribe – it’s free