Next.js 14 App Router: GET & POST Examples (with TypeScript)

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.

Example File Structure:
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.

Example with Query 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.

Example with Form Data:
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].

Example with Dynamic Segments:
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.

Example of Revalidating Cached Data:
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.

Example of Handling Cookies:
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.

Example of Implementing a Redirect:
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.

Example of Setting a Cookie:
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.

Example of Getting a Cookie:
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.

Example of Deleting a Cookie:
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.

Example of Using nextUrl Properties:
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.

Example of Accessing IP Address and Geographic Information:
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.

Example of Setting a Cookie:
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.

Example of Getting a Cookie:
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.

Example of Returning a JSON Response:
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.

Example of Performing a Redirect:
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.

Example of Using next() in Middleware:
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!

Raymond Yeh

Raymond Yeh

Published on 12 August 2024

Get engineers' time back from marketing!

Don't let managing a blog on your site get in the way of your core product.

Wisp empowers your marketing team to create and manage content on your website without consuming more engineering hours.

Get started in few lines of codes.

Choosing a CMS
Related Posts
API in Next.js 15: GET & POST Route Handler Examples

API in Next.js 15: GET & POST Route Handler Examples

Build an API Endpoint in Next.js 15 using the latest route handler. See what's new and get code example to serve your API.

Read Full Story
Next.js 14 App Router: GET & POST Handler Examples

Next.js 14 App Router: GET & POST Handler Examples

Unlock the full potential of Next.js 14 with our guide to the App Router, featuring step-by-step examples of GET and POST handlers to elevate your React projects!

Read Full Story
Server Actions vs API Routes in Next.js 15 - Which Should I Use?

Server Actions vs API Routes in Next.js 15 - Which Should I Use?

Next.js 15 brings Server Actions and API Routes into the spotlight. Dive into our comprehensive analysis to master these powerful tools and boost your app's performance and security!

Read Full Story