Next.js has emerged as a powerful framework for building server-side rendered (SSR) and statically generated applications. With the release of Next.js 15, developers can now leverage a host of new features and improvements that make handling API GET and POST requests more efficient and secure.
This article discusses specifics of these new features, providing detailed examples and insights to help you make implement endpoints to handle GET & POST requests in Next.js 15.
What’s New in Next.js 15 for API Handling
Next.js 15 introduces several significant updates aimed at optimizing API handling, particularly for GET and POST requests. Here are some of the key features:
Async Request APIs
One of the most notable changes in Next.js 15 is the transition to asynchronous APIs for request-specific data such as headers, cookies, params, and searchParams. This move aims to enhance performance and pave the way for future optimizations.
import { cookies } from 'next/headers';
export async function AdminPanel() {
const cookieStore = await cookies();
const token = cookieStore.get('token');
// ...additional logic
}
Caching Semantics
Caching behavior has undergone significant changes. By default, route handlers are no longer cached. However, you can opt into caching GET methods by using a dynamic route config option like export const dynamic = 'force-static';
.
export const dynamic = 'force-static';
export async function GET() {
const res = await fetch('https://data.mongodb-api.com/...', {
headers: {
'Content-Type': 'application/json',
'API-Key': process.env.DATA_API_KEY,
},
});
const data = await res.json();
return Response.json({ data });
}
unstable_after
API
The experimental unstable_after
API allows you to schedule tasks to run after the response has finished streaming. This is particularly useful for secondary tasks like logging and analytics.
import { unstable_after as after } from 'next/server';
import { log } from '@/app/utils';
export default function Layout({ children }) {
after(() => {
log();
});
return <>{children}</>;
}
To enable this feature, update your next.config.ts
:
const nextConfig = { experimental: { after: true } };
export default nextConfig;
Understanding API Routes in Next.js 15
Routing and Route Handlers
Route handlers in Next.js 15 are located within the app
directory and are defined in a route.js
or route.ts
file, depending on whether you are using JavaScript or TypeScript.
// route.ts
export async function GET(request: Request) {}
Supported HTTP Methods
Next.js 15 supports the following HTTP methods within Route Handlers:
GET
POST
PUT
PATCH
DELETE
HEAD
OPTIONS
Extended APIs: NextRequest
and NextResponse
Next.js extends the native Request
and Response
APIs with NextRequest
and NextResponse
, providing additional functionalities that simplify the handling of various aspects of HTTP requests and responses.
Setting Up Your Project
Project Initialization
To get started with a new Next.js 15 project, follow these steps:
npx create-next-app@latest my-next-app
Directory Structure
Here is an overview of the typical directory structure for a Next.js 15 project:
my-next-app/
├── app/
│ ├── api/
│ │ ├── hello/
│ │ │ └── route.ts
│ ├── favicon.ico
│ ├── globals.css
│ ├── layout.js
│ └── page.js
├── public/
├── styles/
├── .gitignore
├── package.json
└── README.md
Basic GET and POST Handlers
Basic GET Handler
Let's start with a simple GET handler. This handler responds with a static message:
// app/api/hello/route.ts
export async function GET(request: Request) {
return new Response('Hello, Next.js 15!');
}
Fetching Data from an External API
A more practical GET handler involves fetching data from an external API. In this example, we fetch blog posts from an external API and return them as a JSON response:
// app/posts/route.ts
export const revalidate = 60;
export async function GET() {
const data = await fetch('https://api.vercel.app/blog');
const posts = await data.json();
return Response.json(posts);
}
Basic POST Handler
For POST requests, let's look at a handler that reads JSON data from the request body and responds with the same data:
// app/items/route.ts
export async function POST(request: Request) {
const res = await request.json();
return Response.json({ res });
}
Handling Form Data
Next.js 15 makes it straightforward to handle multipart form data as well. Here's an example of a POST handler that reads form data:
// app/items/route.ts
export async function POST(request: Request) {
const formData = await request.formData();
const name = formData.get('name');
const email = formData.get('email');
return Response.json({ name, email });
}
Advanced Handling Techniques
Advanced GET Handlers
Caching ConfigurationsTo configure caching for GET requests, you can use the following setup:
// app/items/route.ts
export const dynamic = 'force-static';
export async function GET() {
const res = await fetch('https://data.mongodb-api.com/...', {
headers: { 'Content-Type': 'application/json', 'API-Key': process.env.DATA_API_KEY },
});
const data = await res.json();
return Response.json({ data });
}
Handling Query ParametersTo handle query parameters within your GET handler, you can extract them from the request URL:
// app/items/route.ts
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const category = searchParams.get('category');
const res = await fetch(`https://api.example.com/items?category=${category}`);
const items = await res.json();
return new Response(JSON.stringify(items), {
headers: { 'Content-Type': 'application/json' },
});
}
Reading and Setting HeadersReading headers from a request and setting custom headers on the response is simple in Next.js 15:
// app/api/route.ts
import { headers } from 'next/headers';
export async function GET(request: Request) {
const headersList = await headers();
const referer = headersList.get('referer');
return new Response('Hello, Next.js!', { status: 200, headers: { referer: referer } });
}
Handling RedirectsYou can programmatically redirect requests within your route handlers using the redirect
function:
// app/api/route.ts
import { redirect } from 'next/navigation';
export async function GET(request: Request) {
redirect('https://nextjs.org/');
}
Using Dynamic SegmentsDynamic segments in your route can be handled by accessing the params
object:
// app/items/[slug]/route.ts
export async function GET(request: Request, { params }: { params: Promise<{ slug: string }> }) {
const slug = (await params).slug;
// Handle the request based on slug
}
Advanced POST Handlers
Validation and Form Data ProcessingIn a POST handler, you can validate and process form data easily:
// app/api/form/route.ts
export async function POST(request: Request) {
const body = await request.json();
const { name, email } = body;
if (!name || !email) {
return new Response('Missing fields', { status: 400 });
}
// Process the valid form data (e.g., save to database)
return new Response(`Form submitted successfully. Name: ${name}, Email: ${email}`, { status: 200 });
}
Integrating with a Database (e.g., Prisma)For more advanced POST handlers, you can integrate with a database like Prisma to create new records:
// app/api/users/route.ts
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
export async function POST(request: Request) {
try {
const body = await request.json();
const { name, email } = body;
const newUser = await prisma.user.create({
data: {
name,
email,
},
});
return new Response(JSON.stringify(newUser), {
headers: { 'Content-Type': 'application/json' },
status: 201,
});
} catch (error) {
return new Response(`Error: ${error.message}`, { status: 500 });
} finally {
await prisma.$disconnect();
}
}
Special Route Handler Scenarios
Handling CookiesYou can manage cookies within your route handlers using the cookies
API:
// app/api/route.ts
import { cookies } from 'next/headers';
export async function GET(request: Request) {
const cookieStore = await cookies();
const token = cookieStore.get('token');
return new Response('Hello, Next.js!', { status: 200, headers: { 'Set-Cookie': `token=${token.value}` } });
}
Implementing CORSTo handle Cross-Origin Resource Sharing (CORS) in your API routes, you can set the necessary headers in your response:
// app/api/route.ts
export async function GET(request: Request) {
return new Response('Hello, Next.js!', {
status: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
});
}
Creating WebhooksYou can create webhook endpoints by setting up POST request handlers that process incoming data:
// app/api/route.ts
export async function POST(request: Request) {
try {
const text = await request.text();
// Process the webhook payload
} catch (error) {
return new Response(`Webhook error: ${error.message}`, { status: 400 });
}
return new Response('Success!', { status: 200 });
}
Conclusion
Next.js 15 introduces a range of new features, improvements, and breaking changes aimed at enhancing performance, security, and developer experience. By understanding the new API GET and POST request handling techniques, you can leverage these updates to build more efficient and capable applications.