What Makes Next.js APIs So Special?
I'm assuming you know what APIs are. APIs are an important part of developing functional web apps. Next.js has introduced the feature to create route handlers for APIs since version 9.0 in 2019. There are lots of ways to build a separate backend (like Ruby, PHP, Express.js, Django, etc.) other than Next.js, so why even do it in Next.js?.
It might not be that great
By default, Next.js route handlers use serverless functions, which means in some cases you might have cold starts, high cost for scaling, and vendor lock-in since it requires serverless platforms like Vercel to work properly. It also has limitations when it comes to using complex middleware, unlike a library like Express.js, which was meant to be used for routing on a server.
But it's not that bad
But most of these issues are automatically taken care of by Vercel (which provides amazing caching & optimization strategies) and serverless functions can be scalable and cost-effective if done properly. Here are some more benefits of using Next.js as both your frontend and backend:
- Hot reloading: This means when developing, you get the same hot reload features as your frontend for your API routes, which makes development faster.
- Consistency of your codebase: It greatly simplifies development by keeping the frontend and backend logic in the same place.
- File-based routing: API routes use a simple file-based routing system that easily integrates into your Next.js project structure.
- No additional setup: There's no need to configure additional middleware or create a separate server.
- Simplication: It has built-in features to simplify common tasks when developing your API.
API routes in the Next.js app router
The app router uses a file-based routing system that starts from the app
directory. You can mirror URL paths by defining API routes in a nested folder structure. By doing this, you create Node.js serverless functions that act as API endpoints.
app/
└── api/
└── hello/
└── route.js
Creating API routes
The API route file is usually named route.js
or route.ts
(for TypeScript users). This has to be in an api
folder in the app
folder. You can define functions in the route.js
files that handle different HTTP methods (such as GET, POST, PUT, PATCH, etc.). For example, when you can make a GET
request to /api/hello
it would be handled by a GET
function in app/api/hello/route.js
.
Request methods and API route functions
The standard convention is that within the route.js
or route.ts
file, you name an exported function with the HTTP method you want it to handle in your API endpoint in uppercase. The functions usually look like this:
// app/api/users/route.js
export async function GET(request) {
// Simulate a database call to fetch users
const users = [
{ id: 1, name: 'John Doe' },
{ id: 2, name: 'Jane Doe' },
];
// Return the users as a JSON response
return NextResponse.json(users);
}
NextResponse and Request
The NextResponse
is a helper class that extends the web Response API with additional convenience methods, which are used to create responses. You can use it to rewrite, redirect, set headers, or create and send JSON responses.
The Request
: When you define functions by using HTTP methods in creating your API endpoints, the functions receive a Request
object as an argument which contains information about the incoming request. The object uses the web Request API. You can use it to read the request query parameters, read the request body, or access the request headers.
Dynamic routes
By using square brackets, when naming your folders before the route.ts
or route.js
file, you can create a dynamic route for your API.
app/
└── api/
└── users/
└── [userId]/
└── route.ts
Accessing a dynamic route
The dynamic route parameters can be accessed from the route handler function's second argument, which has the params
object.
Take a look at this example:
// app/api/users/[userId]/route.ts
export async function GET(request: Request, { params }: { params: { userId: string }}) {
const {userId} = params
// Simulate a database call to fetch users
const users = [
{ id: 1, name: 'John Doe' },
{ id: 2, name: 'Jane Doe' },
];
//filter user by id
const user = users.filter((user) => user.id == userId)
// Return the users as a JSON response
return NextResponse.json(user);
}
I hope that wasn't complicated, and you got something out of this.
You can hear more from me on:
Twitter (X) | Instagram
Top comments (0)