Course
Route handlers
Next.js Mastery: From Fundamentals to Full-Stack
Unlock the power of Next.js with this comprehensive course! Starting with the basics, you’ll learn essential skills such as routing, data fetching, and styling. Progress through practical projects, including building your own React Notes app, to gain hands-on experience. Dive into the source code to understand the inner workings and core principles of Next.js. Perfect for developers with a basic knowledge of React and Node.js, this course ensures you’ll be ready to create high-performance full-stack applications efficiently. Join us and master Next.js, backed by industry experts and a supportive learning community.
Route Handlers
Defining a Route Handler
To get started, you need to create a file named
route.ts
(note the singular form: "route," not "router"). This file should be placed within the app
directory and can be nested in subdirectories under app
. However, keep in mind that page.tsx
and route.ts
cannot reside at the same level because both respond to routes: page.tsx
manages UI rendering, while route.ts
handles requests. Having both at the same level would confuse Next.js about which one to prioritize.GET Request
Let's begin with setting up a GET request, like fetching a list of articles. Create the file
app/api/blogs/route.ts
and add the following code:import { NextResponse } from 'next/server';
interface Post { userId: number; id: number; title: string; body: string;}
export async function GET() { const res = await fetch('https://jsonplaceholder.typicode.com/posts'); const data: Post[] = await res.json();
return new NextResponse(JSON.stringify(data));}
To see the returned data, visit
http://localhost:3000/api/blogs
in your browser.
Playground:
In this example:
- We export an async function named
GET
to handle the GET request. Note the use ofexport
rather thanexport default
. - We utilize the
NextResponse
object fromnext/server
to structure the response content. Alternatively, you can use the standardResponse
object:
export async function GET() { const res = await fetch('https://jsonplaceholder.typicode.com/posts'); const data: Post[] = await res.json();
return new Response(JSON.stringify(data));}
Using
NextResponse
is generally recommended as it offers TypeScript-friendly features and conveniences like cookie handling, being a wrapper around the standard Response
.
Organizing the endpoint under the
app/api
folder is for clarity—it helps differentiate between API endpoints and pages. However, placing it in app/blogs/route.ts
would work too, with the endpoint being /blogs
.
Supported Methods
Next.js supports various HTTP methods such as GET, POST, PUT, PATCH, DELETE, HEAD, and OPTIONS. Unsupported methods automatically return a 405 Method Not Allowed response.
Here's an example:
export async function GET(request) {}export async function HEAD(request) {}export async function POST(request) {}export async function PUT(request) {}export async function DELETE(request) {}export async function PATCH(request) {}export async function OPTIONS(request) {}
POST Request
Let's create a POST request. Update
app/api/blogs/route.js
with:export async function POST(request: Request) { const article = await request.json()
return NextResponse.json({ id: Math.random().toString(36).slice(-8), data: article }, { status: 201 })}
Note: Make sure your application is running on 3000 port
Parameters
Each request handler can take two optional parameters:
request
and context
.export async function GET(request, context) {}
- Request Object
The
request
object, an extension of the Web Request API, is a NextRequest
object that allows easy access to cookies and URL handling. For example:
export async function GET(request, context) { // When accessing /home, the value of pathname is /home const pathname = request.nextUrl.pathname // When accessing /home?name=lee, the value of searchParams is { 'name': 'lee' } const searchParams = request.nextUrl.searchParams}
- Context Object
Currently,
context
contains only params
, which holds dynamic route parameters:// app/dashboard/[team]/route.jsexport async function GET(request, { params }) { const team = params.team}
When accessing
/dashboard/1
, params
is { team: '1' }
.
Example Scenarios
Here are some examples of how parameters work:
Review
To consolidate these concepts, let's modify our GET request to return specific data fields.
The requirement is that currently, when sending a GET request to
/api/blogs
, all blog data is returned. Now, we want the GET request to /api/posts/1?dataField=title
to specifically retrieve the article data for blog id with 1
, with dataField indicating which fields of data should be returned.
Let's begin by creating a new file named
/api/blogs/[blogId]/route.ts
with the following code:export async function GET( request: NextRequest, { params }: { params: { blogId: string } }) { const blogId = params.blogId;
// Check if the request contains a "dataField" search parameter const dataField = request.nextUrl.searchParams.get('dataField');
const res = await fetch( `https://jsonplaceholder.typicode.com/posts/${blogId}` ); const data: Post = await res.json();
// If "dataField" is present, return only that specific field if (dataField) { const result = { [dataField]: data[dataField as keyof Post] }; return new NextResponse(JSON.stringify(result), { headers: { 'Content-Type': 'application/json' }, }); }
// Otherwise, return the entire data return new NextResponse(JSON.stringify(data), { headers: { 'Content-Type': 'application/json' }, });}
Using Postman, if you request
http://localhost:3000/api/posts/1?dataField=title
, you'll get the title of the post with id 1. If you just request http://localhost:3000/api/posts/1
, you'll receive the entire post data.
This guide should help you understand how to define and manage route handlers in Next.js. If you need further examples or clarification, feel free to ask!