Why Build an API in Next.js?
Next.js allows you to create server-side code that runs in a serverless environment. This means you can build a complete backend for your application—for handling form submissions, interacting with a database, or integrating with third-party services—without ever leaving your Next.js project.
1. The Pages Router: API Routes
In the Pages Router, any file inside the pages/api directory is treated as an API endpoint instead of a React page.
- File-based Routing: Just like pages, the file structure maps to the URL.
- pages/api/hello.js → /api/hello
- pages/api/users/[id].js → /api/users/123
- The Handler Function: Each file exports a default function that receives req (request) and res (response) objects, similar to Node.js/Express.
Code Example (pages/api/users.js):
JavaScript
// This function handles all requests to /api/users
export default function handler(req, res) {
  if (req.method === 'GET') {
    // Handle GET request
    const users = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }];
    res.status(200).json(users);
  } else if (req.method === 'POST') {
    // Handle POST request
    const newUser = req.body.name;
    // ... logic to save the new user to a database ...
    res.status(201).json({ message: 'User created!', name: newUser });
  } else {
    // Handle other methods
    res.setHeader('Allow', ['GET', 'POST']);
    res.status(405).end(`Method ${req.method} Not Allowed`);
  }
}
2. The App Router: Route Handlers
The App Router introduces a more modern approach with Route Handlers. Instead of a single handler function, you create a route.js (or .ts) file and export named functions corresponding to the HTTP methods you want to handle.
- Folder-based Routing: Like pages, folders define the route. The endpoint itself is a route.js file.
- app/api/hello/route.js → /api/hello
- Named Export Functions: You export async functions named GET, POST, PUT, DELETE, etc.
- Modern Request/Response: These functions use the standard Web Request and Response APIs.
Code Example (app/api/users/route.js):
JavaScript
import { NextResponse } from 'next/server';
// In-memory data for demonstration
let users = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }];
// Handles GET requests to /api/users
export async function GET(request) {
  return NextResponse.json(users);
}
// Handles POST requests to /api/users
export async function POST(request) {
  const { name } = await request.json(); // Parse the request body
  const newUser = { id: Date.now(), name };
  // ... logic to save the new user to a database ...
  users.push(newUser);
  return NextResponse.json({ message: 'User created', user: newUser }, { status: 201 });
}
Dynamic API Routes
Both routers support dynamic segments for creating endpoints that accept parameters, like an ID.
App Router Example (app/api/users/[id]/route.js): The params object is passed as the second argument to your handler.
JavaScript
import { NextResponse } from 'next/server';
// Handles GET requests to /api/users/1, /api/users/2, etc.
export async function GET(request, { params }) {
  const userId = params.id; // Access the dynamic 'id' parameter
  // ... logic to find the user by id from a database ...
  const user = { id: userId, name: 'Example User' };
  if (!user) {
    return NextResponse.json({ error: 'User not found' }, { status: 404 });
  }
  return NextResponse.json(user);
}