In addition to creating page routes, which means pages are served to the browser as Web pages, Next.js can create API routes.
This is a very interesting feature because it means that Next.js can be used to create a frontend for data that is stored and retrieved by Next.js itself, transferring JSON via fetch requests.
API routes live under the /pages/api/ folder and are mapped to the /api endpoint.
This feature is very useful when creating applications.
In those routes, we write Node.js code (rather than React code). It’s a paradigm shift, you move from the frontend to the backend, but very seamlessly.
Say you have a /pages/api/comments.js file, whose goal is to return the comments of a blog post as JSON.
Say you have a list of comments stored in a comments.json file:
[
{
"comment": "First"
},
{
"comment": "Nice post"
}
]
Here’s a sample code, which returns to the client the list of comments:
import comments from './comments.json'
export default (req, res) => {
res.status(200).json(feeds)
}
It will listen on the /api/comments URL for GET requests, and you can try calling it using your browser:

API routes can also use dynamic routing like pages, use the [] syntax to create a dynamic API route, like /pages/api/comments/[id].js which will retrieve the comments specific to a post id.
Inside the [id].js you can retrieve the id value by looking it up inside the req.query object:
import comments from '../comments.json'
export default (req, res) => {
res.status(200).json({ post: req.query.id, comments })
}
Heres you can see the above code in action:

In dynamic pages, you’d need to import useRouter from next/router, then get the router object using const router = useRouter(), and then we’d be able to get the id value using router.query.id.
In the server-side it’s all easier, as the query is attached to the request object.
If you do a POST request, all works in the same way - it all goes through that default export.
To separate POST from GET and other HTTP methods (PUT, DELETE), lookup the req.method value:
export default (req, res) => {
switch (req.method) {
case 'GET':
//...
break
case 'POST':
//...
break
default:
res.status(405).end() //Method Not Allowed
break
}
}
In addition to req.query and req.method we already saw, we have access to cookies by referencing req.cookies, the request body in req.body.
Under the hoods, this is all powered by Micro, a library that powers asynchronous HTTP microservices, made by the same team that built Next.js.
You can make use of any Micro middleware in our API routes to add more functionality.