Route Handlers (API Endpoints)
Next.js can be your backend too — a route.js file becomes a real API endpoint that returns JSON.
What you will learn
- Create an API endpoint with route.js
- Return JSON with Response.json
- Call your own API from a page
Your backend lives in the same project
A normal React app needs a separate backend server for things like saving data or hiding API keys. Next.js bundles a backend in: a file named route.js (instead of page.js) becomes an API endpoint — a URL that returns data, not a page. (API = Application Programming Interface, a way for programs to talk to each other. The data is usually sent as JSON — JavaScript Object Notation, a simple text format for data that looks just like a JavaScript object.)
A GET endpoint
Inside route.js you export a function named after the HTTP method, like GET. Return data with Response.json(...) and it is sent as JSON.
// app/api/products/route.js
export async function GET() {
const products = [
{ id: 1, name: 'Keyboard', price: 999 },
{ id: 2, name: 'Mouse', price: 499 },
];
return Response.json(products);
}Note: Output (visiting /api/products in the browser): [ { "id": 1, "name": "Keyboard", "price": 999 }, { "id": 2, "name": "Mouse", "price": 499 } ] This URL returns JSON, not a web page. A page, a mobile app, or another service can now fetch this data.
The request-response flow, step by step
Every API call follows the same simple round trip: a request goes out (someone asks for data) and a response comes back (your server answers). Here is exactly what happens when something visits /api/products:
- A client (a browser tab, a phone app, or another server) sends an HTTP request to
/api/products. Because it is just opening a URL, that is aGETrequest. - Next.js sees the request is a
GETand runs theGETfunction you exported inroute.js. - Your function builds the
productsarray — in a real app this is where you would read from a database. - You call
Response.json(products), which packs the array into a JSON response. - Next.js sends that response back to the client. The client now has the data and can show it, save it, or do anything it likes with it.
| File | Becomes | Returns |
|---|---|---|
app/about/page.js | A web page at /about | HTML you see |
app/api/products/route.js | An API at /api/products | JSON data |
Handling other methods
Export POST, PUT or DELETE in the same file to handle those requests — for example, to create a new product when the browser sends a POST.
// app/api/products/route.js
export async function POST(request) {
const body = await request.json(); // data the client sent
// ...save body to a database here...
return Response.json({ created: body }, { status: 201 });
}Note: Output (after sending a POST with { "name": "Monitor" }): { "created": { "name": "Monitor" } } Status: 201 Created The handler read the sent data with request.json() and replied with a 201 status, the standard "created" code.
Tip: Route handlers are the perfect place for secrets. Put your database password or paid API key here — this code runs on the server and is never sent to the browser.
Watch out: A folder can have a page.js or a route.js, not both in the same folder — one serves a page, the other serves data. Keep your API under an app/api/... area to stay tidy.
Q. Which file turns a folder into a JSON API endpoint instead of a web page?
✍️ Practice
- Create
app/api/hello/route.jsthat returns{ message: "Hi!" }and visit it. - Add a
GETthat returns a list of three books as JSON.
🏠 Homework
- Build an
/api/productsendpoint returning a list, then fetch and display it on a page.