Using Route Handlers: Building Custom APIs in Next.js
Learn how to create custom API endpoints in Next.js using Route Handlers. Master HTTP methods, request parsing, and JSON responses for your backend tasks.
Previously in this course, we explored structuring large projects to keep our code maintainable as our blog grows. While most of our interaction with data has happened through Server Actions, sometimes you need a standard RESTful endpoint—for example, to support a mobile app or a data export service. That is where Route Handlers come in.
What are Route Handlers?
Route Handlers allow you to create custom request handlers for a given route using the standard Web Request and Response APIs. Unlike page.js, which renders UI, a route.js file is purely for backend logic.
They are defined within the app directory, just like pages, but they are not restricted by the standard file-system hierarchy in the same way. A route.js file will always take precedence over a page.js file in the same segment.
Creating Your First Route Handler
To create a route, add a route.js file inside an app/api directory (or any subfolder you choose). Let's build a simple endpoint that returns a list of recent blog posts in JSON format.
JAVASCRIPT// app/api/posts/route.js import { NextResponse } from CE9178">'next/server'; export async function GET() { const posts = [ { id: 1, title: CE9178">'Learning Next.js' }, { id: 2, title: CE9178">'Mastering Route Handlers' } ]; return NextResponse.json({ posts }); }
When you navigate to /api/posts in your browser, you will receive a JSON response. This is the foundation of building a robust API. Before you scale this, consider reading about API design consistency to ensure your endpoint naming follows professional standards.
Handling HTTP Methods
Route Handlers support the standard HTTP methods: GET, POST, PUT, PATCH, DELETE, HEAD, and OPTIONS. You simply export a function named after the HTTP verb you wish to handle.
Here is how you might handle a POST request to create a new resource:
JAVASCRIPT// app/api/posts/route.js export async function POST(request) { const body = await request.json(); // Logic to save to database would go here console.log(CE9178">'Received data:', body); return NextResponse.json({ message: CE9178">'Post created!' }, { status: 201 }); }
When building these, remember that refactoring for clean code is essential; don't dump all your database logic directly into the route.js file.
Comparison: Page vs. Route Handlers
| Feature | page.js | route.js |
|---|---|---|
| Primary Output | HTML/React UI | Data (JSON, XML, etc.) |
| Rendering | Server or Client | Server-side only |
| HTTP Methods | N/A (Handles GET) | Supports GET, POST, etc. |
| Use Case | User-facing pages | Webhooks, APIs, exports |
Hands-on Exercise
- Create a new folder at
app/api/hello. - Inside, create a
route.jsfile. - Export a
GETfunction that returns a JSON object:{ "message": "Hello, World!" }. - Visit
http://localhost:3000/api/helloin your browser to verify the response.
Common Pitfalls
- Caching: By default,
GETrequests in Route Handlers are cached. If you are fetching dynamic data from a database, you may need to useexport const dynamic = 'force-dynamic'at the top of your file to ensure you always get fresh data. - Response Headers: Don't forget that you can pass a second argument to
NextResponse.json()to set custom headers, such as CORS headers, if you need to allow requests from other domains. - Mixing Logic: Avoid putting complex business logic in the
route.jsfile. Treat it as a controller that calls your data layer or service functions.
FAQ
Can I use both page.js and route.js in the same folder?
No. If you have both, the route.js will take precedence and the page.js will be ignored. Keep your API routes in a separate api/ directory to avoid conflicts.
How do I handle query parameters?
You can access them via the NextRequest object's nextUrl property. For example, request.nextUrl.searchParams.get('id').
Are Route Handlers just like Express.js?
They are similar in concept, but they rely on the native Web Request and Response objects rather than a specific framework's request/response middleware pattern.
Recap
We have covered the basics of defining route.js, handling different HTTP methods, and returning JSON data. These endpoints are the backbone of non-page interactions in your Next.js application, allowing you to build everything from public APIs to complex webhook listeners.
Up next: We will put these concepts to work by building a system to listen for webhooks and execute backend tasks.
Work with me

Laravel REST API Development
Clean, secure, well-documented Laravel REST APIs — the backend engine for your app, mobile client, or SaaS. Built by an API specialist.

Next.js Full-Stack Web App Development
A fast, SEO-ready full-stack web app built with Next.js 16 — from idea to deployed product, by an engineer who ships to production.
