Next.js has emerged as a powerful framework for building robust and scalable web applications. With the release of Next.js 14, developers now have access to the new App Router that simplifies routing and enhances application performance. In this article, we'll dive deep into creating GET and POST route handlers using the App Router in Next.js 14, with a focus on utilizing TypeScript for type-safety and improved developer experience.
Whether you're a seasoned developer or just starting out, mastering the basics of GET and POST requests is crucial for building dynamic web applications. We'll explore practical examples, advanced techniques, and best practices to help you make the most out of the new features in Next.js 14.
For those looking to create content-rich web applications, consider exploring Wisp CMS, a powerful tool that can help streamline your development process. But first, let's get started with understanding route handlers in Next.js 14.
Understanding Route Handlers in Next.js 14 App Router
Route Handlers in Next.js 14 allow you to create custom request handlers for a given route using Web APIs. These handlers are analogous to API Routes but are specifically designed for the App Router. They are defined using the route.js|ts
file convention within the app
directory of your Next.js project.
File Location and Naming Convention
Route handlers are defined in a route.js|ts
file and must be placed within the app
directory. The naming convention ensures that Next.js can automatically detect and use these files for routing purposes.
app/
├── api/
│ └── example/
│ └── route.ts
└── ...
Supported HTTP Methods
The following HTTP methods are supported by Route Handlers:
GET
POST
PUT
PATCH
DELETE
HEAD
OPTIONS
When an unsupported method is called, Next.js returns a 405 Method Not Allowed
response.
Extended NextRequest and NextResponse APIs
Next.js extends native Request
and Response
objects with NextRequest
and NextResponse
to provide convenient helpers for advanced use cases. These extended APIs offer additional methods and properties that simplify handling of cookies, headers, and URL parameters.
Next, we'll explore how to create GET and POST route handlers in Next.js 14 using TypeScript.
Working with GET Requests
GET requests are one of the most common HTTP methods used to retrieve data from a server. In Next.js 14, creating a GET route handler is straightforward and can be done using TypeScript for added type safety.
Example GET Route Handler
In this example, we'll create a GET route handler that fetches data from an external API and returns it as JSON:
Code:import { NextRequest, NextResponse } from 'next/server';
export async function GET(request: NextRequest) {
const res = await fetch('https://api.example.com/data', {
headers: {
'Content-Type': 'application/json',
'API-Key': process.env.DATA_API_KEY!,
},
});
const data = await res.json();
return NextResponse.json(data);
}
In this code snippet, we define a GET
function that uses the fetch
API to retrieve data from an external source. The data is then returned as a JSON response using NextResponse.json()
.
Handling Query Parameters
Query parameters are often used to pass data to the server via the URL. The NextRequest
object provides a convenient way to handle these parameters.
import { NextRequest, NextResponse } from 'next/server';
export function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams;
const query = searchParams.get('query');
// Process the query parameter
const data = { query }; // Replace with actual data fetching logic
return NextResponse.json(data);
}
In this example, we use the nextUrl.searchParams
property to access query parameters from the request URL and process them accordingly.
Working with POST Requests
POST requests are essential for sending data to the server, whether it's for creating new resources or processing form submissions. In this section, we'll explore how to create a POST route handler in Next.js 14 using TypeScript.
Example POST Route Handler
In this example, we'll create a POST route handler that accepts JSON data from the request body and sends it to an external API:
Code:import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
const body = await request.json();
const res = await fetch('https://api.example.com/submit', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'API-Key': process.env.DATA_API_KEY!,
},
body: JSON.stringify(body),
});
const data = await res.json();
return NextResponse.json(data);
}
In this code snippet, we define a POST
function that reads the JSON data from the request body using request.json()
. We then send this data to an external API using the fetch
API with a POST method and return the response as JSON using NextResponse.json()
.
Handling Form Data
In addition to JSON, POST requests can also handle form data. The NextRequest
object allows you to work with form data seamlessly.
import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
const formData = await request.formData();
const formEntries = Object.fromEntries(formData.entries());
// Process the form data
const data = { formEntries }; // Replace with actual data processing logic
return NextResponse.json(data);
}
In this example, we use the request.formData()
method to read form data from the request. The Object.fromEntries(formData.entries())
converts the form data into a plain object for easier processing.
Advanced Route Handling Techniques
Next.js 14 provides several advanced techniques for handling routes, including dynamic segments, revalidating cached data, managing cookies and headers, implementing redirects, and even streaming responses. Let's explore some of these advanced techniques.
Dynamic Segments
Dynamic segments allow you to create routes with variable parts, making it possible to handle paths like /api/example/[slug]
.
export async function GET(request: NextRequest, { params }: { params: { slug: string } }) {
const slug = params.slug; // Extract the dynamic segment
// Fetch or process data based on the slug
const data = { slug }; // Replace with actual data fetching logic
return NextResponse.json(data);
}
In this example, we define a GET
function that extracts the dynamic segment (in this case, slug
) from the URL parameters and processes it accordingly.
Revalidating Cached Data
Next.js allows you to revalidate cached data using the next.revalidate
option, ensuring that your application always serves fresh data when needed.
export async function GET() {
const res = await fetch('https://api.example.com/data', {
next: { revalidate: 60 },
headers: {
'Content-Type': 'application/json',
'API-Key': process.env.DATA_API_KEY!,
},
});
const data = await res.json();
return NextResponse.json(data);
}
In this example, the next.revalidate
option is set to 60
seconds, indicating that the cached data should be refreshed every minute.
Managing Cookies and Headers
Handling cookies and headers is simplified with the NextRequest
and NextResponse
APIs. You can easily read, set, or delete cookies and manage headers for your requests and responses.
import { cookies } from 'next/headers';
export async function GET(request: NextRequest) {
const cookieStore = cookies();
const token = cookieStore.get('token'); // Read a cookie
cookieStore.set('token', 'new-value'); // Set a cookie
cookieStore.delete('token'); // Delete a cookie
return NextResponse.json({ token });
}
In this example, we use the cookies
function to read, set, and delete cookies within the route handler.
Implementing Redirects
Redirects can be implemented using the NextResponse.redirect()
method to send the user to a different URL.
import { NextResponse } from 'next/server';
export async function GET(request: NextRequest) {
return NextResponse.redirect(new URL('/new-path', request.url));
}
In this example, a GET request to the route will redirect the user to /new-path
.
Streaming Responses
Next.js supports streaming responses, which can be particularly useful for handling large datasets or real-time data.
Example of Streaming Response:import OpenAI from 'openai';
import { OpenAIStream, StreamingTextResponse } from 'ai';
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
export const runtime = 'edge';
export async function POST(req: NextRequest) {
const { messages } = await req.json();
const response = await openai.chat.completions.create({
model: 'gpt-3.5-turbo',
stream: true,
messages,
});
const stream = OpenAIStream(response);
return new StreamingTextResponse(stream);
}
In this example, we use the OpenAI API to generate a streaming response, which is then returned to the client using StreamingTextResponse
.
NextRequest API in Detail
The NextRequest
API in Next.js 14 extends the native Web Request API with additional convenience methods and properties, making it easier to handle incoming HTTP requests. Let's explore some of the key features and how to use them.
Managing Cookies with NextRequest
The NextRequest
API provides several methods for managing cookies, including setting, getting, and deleting cookies.
import { NextRequest, NextResponse } from 'next/server';
export async function GET(request: NextRequest) {
const response = NextResponse.next();
response.cookies.set('show-banner', 'false');
return response;
}
In this example, we use the response.cookies.set()
method to set a cookie called show-banner
with a value of false
.
import { NextRequest, NextResponse } from 'next/server';
export async function GET(request: NextRequest) {
const cookie = request.cookies.get('show-banner');
return NextResponse.json({ cookie });
}
In this example, we use the request.cookies.get()
method to retrieve the value of the show-banner
cookie and return it as a JSON response.
import { NextRequest, NextResponse } from 'next/server';
export async function GET(request: NextRequest) {
const response = NextResponse.next();
response.cookies.delete('show-banner');
return response;
}
In this example, we use the response.cookies.delete()
method to delete the show-banner
cookie.
Utilizing nextUrl Properties
The nextUrl
property of NextRequest
provides additional convenience methods and properties specific to Next.js, such as pathname
and searchParams
.
import { NextRequest, NextResponse } from 'next/server';
export async function GET(request: NextRequest) {
const pathname = request.nextUrl.pathname; // Gets the pathname of the request URL
const searchParams = request.nextUrl.searchParams; // Gets the search parameters of the request URL
const query = searchParams.get('query');
return NextResponse.json({ pathname, query });
}
In this example, we use the request.nextUrl.pathname
and request.nextUrl.searchParams
properties to access the pathname and search parameters of the request URL, respectively.
Accessing IP Address and Geographic Information
The NextRequest
API also allows you to access the IP address and geographic information of the request, which can be useful for localization and analytics purposes.
import { NextRequest, NextResponse } from 'next/server';
export async function GET(request: NextRequest) {
const ip = request.ip; // Gets the IP address of the request
const geo = request.geo; // Gets the geographic information of the request
return NextResponse.json({ ip, geo });
}
In this example, we use the request.ip
and request.geo
properties to access the IP address and geographic information of the request, respectively.
NextResponse API in Detail
The NextResponse
API in Next.js 14 extends the native Web Response API with additional convenience methods, making it easier to handle HTTP responses. Let's explore some of the key features and how to use them.
Setting and Getting Cookies with NextResponse
The NextResponse
API provides methods for setting and getting cookies from the response.
import { NextResponse } from 'next/server';
export async function GET() {
const response = NextResponse.next();
response.cookies.set('show-banner', 'false');
return response;
}
In this example, we use the response.cookies.set()
method to set a cookie called show-banner
with a value of false
.
import { NextResponse } from 'next/server';
export async function GET() {
const response = NextResponse.next();
const cookie = response.cookies.get('show-banner');
return NextResponse.json({ cookie });
}
In this example, we use the response.cookies.get()
method to retrieve the value of the show-banner
cookie and return it as a JSON response.
Returning JSON Responses with Custom Status Codes
The NextResponse
API makes it easy to return JSON responses with custom status codes.
import { NextResponse } from 'next/server';
export async function GET() {
return NextResponse.json({ message: 'Hello, World!' }, { status: 200 });
}
In this example, we use the NextResponse.json()
method to return a JSON response with a custom status code of 200.
Performing Redirects
The NextResponse
API provides a redirect()
method to perform URL redirects.
import { NextResponse } from 'next/server';
export async function GET(request: NextRequest) {
return NextResponse.redirect(new URL('/new-path', request.url));
}
In this example, we use the NextResponse.redirect()
method to redirect the user to /new-path
.
Using next() in Middleware
The NextResponse.next()
method is used to continue with the next middleware or route handler in the chain.
import { NextResponse } from 'next/server';
export async function middleware(request: NextRequest) {
const newHeaders = new Headers(request.headers);
newHeaders.set('x-version', '123');
return NextResponse.next({
request: {
headers: newHeaders,
},
});
}
In this example, we use the NextResponse.next()
method to forward the request to the next middleware or route handler, adding a custom header in the process.
Examples and Use Cases
To help you better understand how to use GET and POST handlers with NextRequest
and NextResponse
, let's discuss some practical examples and use cases that you can integrate into your Next.js projects.
Example 1: User Authentication
Implementing user authentication is a common use case for POST requests. Let's create a route handler that processes login requests.
Code:import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
const { username, password } = await request.json();
// Perform authentication logic
if (username === 'admin' && password === 'password') {
return NextResponse.json({ success: true });
}
return NextResponse.json({ success: false }, { status: 401 });
}
In this example, we define a POST
function that reads the username
and password
from the request body and performs a simple authentication check. If the credentials are correct, a success response is returned. Otherwise, a 401 Unauthorized response is returned.
Example 2: Fetching User Profile
Fetching user profile data is a common use case for GET requests. Let's create a route handler that retrieves user profile information based on a user ID.
Code:import { NextRequest, NextResponse } from 'next/server';
export async function GET(request: NextRequest) {
const userId = request.nextUrl.searchParams.get('id');
// Fetch user profile data from a database or external API
const profile = { id: userId, name: 'John Doe' }; // Mock data
return NextResponse.json(profile);
}
In this example, we define a GET
function that reads the id
query parameter from the request URL and fetches user profile data based on the user ID. The profile data is then returned as a JSON response.
Example 3: Creating a New Record
Creating a new record in a database is another common use case for POST requests. Let's create a route handler that processes form submissions to create a new record.
Code:import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
const formData = await request.formData();
const formEntries = Object.fromEntries(formData.entries());
// Save the new record to a database
const record = { id: '123', ...formEntries }; // Mock data
return NextResponse.json(record);
}
In this example, we define a POST
function that reads form data from the request, processes it, and creates a new record. The newly created record is then returned as a JSON response.
Example 4: Updating a Record
Updating an existing record is another common use case for POST requests. Let's create a route handler that updates a record based on provided data.
Code:import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
const { id, ...updates } = await request.json();
// Update the record in the database
const updatedRecord = { id, ...updates }; // Mock data
return NextResponse.json(updatedRecord);
}
In this example, we define a POST
function that reads the record ID and updates from the request body, processes the update, and returns the updated record as a JSON response.
Conclusion
In this article, we've explored the powerful features of the Next.js 14 App Router, including how to create GET and POST route handlers using TypeScript. We've covered practical examples, advanced techniques, and best practices to help you build robust and scalable web applications.
As you continue to explore Next.js and its capabilities, consider using Wisp CMS for creating content-rich web applications. Wisp CMS offers a range of features that can streamline your development process and enhance your application's performance.
Thank you for reading, and happy coding!