Back to Blog
Lesson 50 of the Next.js: Build Full-Stack Apps with the App Router course
Next.jsSeptember 6, 20264 min read

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.

Next.jsAPIRoute HandlersBackendWeb Development

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

Featurepage.jsroute.js
Primary OutputHTML/React UIData (JSON, XML, etc.)
RenderingServer or ClientServer-side only
HTTP MethodsN/A (Handles GET)Supports GET, POST, etc.
Use CaseUser-facing pagesWebhooks, APIs, exports

Hands-on Exercise

  1. Create a new folder at app/api/hello.
  2. Inside, create a route.js file.
  3. Export a GET function that returns a JSON object: { "message": "Hello, World!" }.
  4. Visit http://localhost:3000/api/hello in your browser to verify the response.

Common Pitfalls

  • Caching: By default, GET requests in Route Handlers are cached. If you are fetching dynamic data from a database, you may need to use export 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.js file. 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.

Similar Posts