When working with API routes in Next.js 15, handling different types of request payloads (JSON, FormData, and Raw text) can be frustrating. Developers often write repetitive code to extract data from req.json()
, req.formData()
, or req.text()
.
To simplify this process, we’ll create a set of utility functions that make request body handling more structured and error-proof. These functions will also integrate with the serverResponse function we built in our previous blog to maintain consistent API responses.
Why Use a Request Body Helper?
❌ Manually parsing request bodies in every API route leads to redundant code.
❌ Missing or invalid payloads can cause unexpected runtime errors.
❌ Inconsistent error messages make debugging difficult.
✅ With reusable helpers, we ensure cleaner code, standardized error handling, and better DX (Developer Experience).
Building the Request Body Helper in Next.js 15
Step 1: Creating the ServerError
Class
First, let’s define a custom error class to handle different request body errors:
export class ServerError extends Error {
constructor(message: string, name: string) {
super(message);
this.name = name;
}
}
📌 This allows us to throw custom errors when request bodies are missing or invalid.
Step 2: Creating Utility Functions for Parsing Request Bodies
Now, we’ll write functions to handle different request body types.
import { ServerError } from "@/helpers/server-helper/ServerError";
export async function getJsonBodyData<T>(req: Request): Promise<T | null> {
try {
return req.json() as T | null;
} catch {
throw new ServerError("JSON Payload is required", "BodyError");
}
}
export async function getFormData<T>(req: Request): Promise<T | null> {
try {
return req.formData() as T | null;
} catch {
throw new ServerError("FORM Payload is required", "BodyError");
}
}
export async function getRawBody(req: Request): Promise<string> {
try {
return req.text();
} catch {
throw new ServerError("TEXT Payload is required", "BodyError");
}
}
✅ Each function automatically throws an error if the required payload is missing or invalid.
✅ Works with TypeScript for better type safety.
Step 3: Integrating with the serverResponse
Function
In our previous blog, we built a reusable serverResponse
function to ensure consistent API responses. Now, let’s use it in an API route.
import serverResponse from "@/helpers/serverResponse";
import { getJsonBodyData } from "@/helpers/request-helper";
export async function POST(req: Request) {
try {
const body = await getJsonBodyData<{ email: string; password: string }>(req);
if (!body || !body.email || !body.password) {
return serverResponse({
success: false,
message: "Missing required fields",
status: 400,
});
}
// Simulated user creation
const user = { id: 1, email: body.email };
return serverResponse({
success: true,
message: "User created successfully",
data: user,
status: 201,
});
} catch (error) {
return serverResponse({
success: false,
message: error instanceof ServerError ? error.message : "Internal Server Error",
error: error.name || "UnknownError",
status: 500,
});
}
}
📌 Now, all API routes will follow a structured error-handling pattern!
Conclusion
With this helper function setup:
✔️ Less boilerplate when handling request payloads.
✔️ Standardized error handling using ServerError
.
✔️ Consistent API responses with serverResponse
.
If you haven’t read the previous blog, I recommend checking it out first to see how serverResponse
works!
💡 What’s your approach to handling API request payloads? Let me know in the comments!
Top comments (0)