Next.js API Routes

API Routes let you write backend code directly inside your Next.js project. You do not need a separate Express server or a standalone backend application. The same project that serves your pages also handles your server-side logic.

What Is an API Route

An API route is a server-side function that receives HTTP requests and sends back responses. When your frontend needs data — from a database, a third-party service, or a calculation — it calls an API route. The API route does the work and returns the result.

FRONTEND PAGE            API ROUTE              DATABASE
─────────────────────────────────────────────────────────
User clicks button  →  /api/users runs  →  Fetches users
                    ←  Returns JSON     ←  Sends data
Page displays data

Creating Your First API Route

Inside the app/ folder, create a folder named api. Inside that, create a subfolder for your route, then add a file named route.js.

app/
  api/
    users/
      route.js     →  yoursite.com/api/users
    products/
      route.js     →  yoursite.com/api/products

The file is named route.js — not page.js. This tells Next.js the file handles API requests, not a rendered page.

Writing a GET Handler

Inside route.js, export named functions matching HTTP methods. The most common method is GET, used to retrieve data.

import { NextResponse } from 'next/server';

export async function GET() {
  const users = [
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' },
  ];

  return NextResponse.json(users);
}

Visit localhost:3000/api/users in your browser and you see the JSON response. The NextResponse.json() method sets the correct headers and converts your data to JSON automatically.

Diagram: HTTP Methods and What They Do

GET     →  Read data        (get a list of users)
POST    →  Create new data  (add a new user)
PUT     →  Replace data     (update a whole user record)
PATCH   →  Update part      (change only the email)
DELETE  →  Remove data      (delete a user)

Each method gets its own exported function in route.js

Writing a POST Handler

A POST handler receives data sent from the client. You read the request body using request.json().

import { NextResponse } from 'next/server';

export async function POST(request) {
  const body = await request.json();
  const { name, email } = body;

  // Save to database here...
  console.log('New user:', name, email);

  return NextResponse.json(
    { message: 'User created', name },
    { status: 201 }
  );
}

Reading URL Parameters

Dynamic API routes work the same way as dynamic page routes — use square brackets in the folder name.

app/api/users/[id]/route.js  →  /api/users/42

export async function GET(request, { params }) {
  const userId = params.id;   // "42"

  // Fetch user with that ID from database...
  return NextResponse.json({ id: userId, name: 'Alice' });
}

Reading Query Parameters

Query parameters appear after the ? in a URL. Use the URL object to read them.

// Request: /api/products?category=shoes&limit=10

export async function GET(request) {
  const { searchParams } = new URL(request.url);
  const category = searchParams.get('category'); // "shoes"
  const limit = searchParams.get('limit');       // "10"

  return NextResponse.json({ category, limit });
}

Sending Error Responses

Return a response with an appropriate HTTP status code when something goes wrong. Status 400 means bad request. Status 404 means not found. Status 500 means server error.

export async function GET(request, { params }) {
  const user = await findUser(params.id);

  if (!user) {
    return NextResponse.json(
      { error: 'User not found' },
      { status: 404 }
    );
  }

  return NextResponse.json(user);
}

Calling an API Route from a Page

From a client component, use the native fetch function to call your API routes.

'use client';
import { useState } from 'react';

export default function UsersList() {
  const [users, setUsers] = useState([]);

  async function loadUsers() {
    const res = await fetch('/api/users');
    const data = await res.json();
    setUsers(data);
  }

  return (
    <div>
      <button onClick={loadUsers}>Load Users</button>
      <ul>
        {users.map(u => <li key={u.id}>{u.name}</li>)}
      </ul>
    </div>
  );
}

When to Use API Routes

USE API ROUTES WHEN:
  ✓ You need to protect secrets (API keys, DB passwords)
  ✓ You accept form submissions
  ✓ You process webhooks from Stripe, GitHub, etc.
  ✓ You build a public API for other apps to consume

SKIP API ROUTES WHEN:
  ✗ Fetching data for a server component (fetch directly)
  ✗ Simple mutations (use Server Actions instead)

Key Takeaway

API routes live in app/api/ folders inside route.js files. Export functions named after HTTP methods — GET, POST, DELETE — to handle different request types. Use NextResponse.json() to return data. API routes run on the server, so your secrets and database credentials stay safe.

Leave a Comment