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.
