🚀 Building a Robust API Response Helper in Next.js 15
Saiful Islam

Saiful Islam @saiful7778

About: I am Saiful Islam, a front-end developer skilled in React.js, TypeScript, JavaScript, and Tailwind CSS. I build user-friendly web apps and have worked on Pocket School Quiz, DTR CLI, and DevMingle.

Location:
Lakshmipur, Bangladesh
Joined:
Mar 8, 2024

🚀 Building a Robust API Response Helper in Next.js 15

Publish Date: Feb 17
11 5

When developing APIs in Next.js 15, structuring responses properly is crucial for consistency, debugging, and maintainability. Instead of manually crafting responses in every route, we can create a universal response helper that ensures uniformity across all API responses.

In this guide, we'll build a reusable API response helper using TypeScript, Next.js 15, and Zod for validation handling.


🔍 Why Use a Response Helper?

Ensures consistency – Every API response follows the same structure
Improves readability – Cleaner, more structured API responses
Simplifies error handling – Centralized handling of validation and server errors
Reduces redundancy – Write less repetitive response code


🛠 Creating the Response Helper

We'll create a utility function that:
✔ Returns a consistent response format
✔ Supports Zod validation errors
✔ Handles success, errors, and status codes dynamically

📌 Step 1: Define TypeScript Types

We need to define strongly typed response structures using TypeScript.

import type { ZodIssue } from "zod";

export type ValidationError = {
  field: string;
  message: string;
};

export interface ServerResponseType<T> {
  success: boolean;
  message?: string | undefined;
  error?: string | undefined | ZodIssue[] | ValidationError[];
  data?: T | undefined;
  status?: number | undefined;
}
Enter fullscreen mode Exit fullscreen mode

🔹 Key Features:

success: Indicates whether the request was successful
message: A human-readable response message
error: Supports custom validation errors or Zod validation issues
data: Stores the actual response data
✔ status: Allows custom HTTP status codes


📌 Step 2: Implement the Helper Function

Now, let's create the API response helper:

import type { ServerResponseType } from "@/types";
import { NextResponse } from "next/server";

/**
 * A universal API response helper function for Next.js 15.
 * @param {ServerResponseType<T>} options - API response parameters.
 * @returns {NextResponse<ServerResponseType<T>>}
 */
export default function serverResponse<T>({
  success,
  message = undefined,
  error = undefined,
  data = undefined,
  status = 200,
}: ServerResponseType<T>): NextResponse<ServerResponseType<T>> {
  const response: ServerResponseType<T> = { success, status };

  if (message) response.message = message;
  if (error) response.error = error;
  if (data) response.data = data;

  return NextResponse.json(response, { status: response.status });
}
Enter fullscreen mode Exit fullscreen mode

🔹 How It Works:

✅ Accepts a generic type <T> for flexible data handling
✅ Automatically formats the response into JSON
✅ Supports custom status codes and error messages
✅ Returns a structured API response


📌 Step 3: Using the Helper in Next.js 15 API Routes

Let's integrate this helper into an API route in Next.js 15.

✅ Example: User Registration API

Here's how we use serverResponse in a Next.js API route:

import { NextRequest } from "next/server";
import serverResponse from "@/utils/serverResponse";
import db from "@/lib/db";
import { z } from "zod";

const userSchema = z.object({
  name: z.string().min(2, "Name must be at least 2 characters"),
  email: z.string().email("Invalid email"),
  password: z.string().min(6, "Password must be at least 6 characters"),
});

export async function POST(req: NextRequest) {
  try {
    const body = await req.json();

    // Validate input data
    const validation = userSchema.safeParse(body);
    if (!validation.success) {
      return serverResponse({
        success: false,
        error: validation.error.issues,
        status: 400,
      });
    }

    // Check if user already exists
    const existingUser = await db.user.findUnique({
      where: { email: body.email },
    });

    if (existingUser) {
      return serverResponse({
        success: false,
        message: "User already exists",
        status: 409,
      });
    }

    // Create new user
    const user = await db.user.create({
      data: {
        name: body.name,
        email: body.email,
        password: body.password, // In real apps, always hash passwords!
      },
    });

    return serverResponse({
      success: true,
      data: user,
      message: "User registered successfully",
      status: 201,
    });
  } catch (error) {
    return serverResponse({
      success: false,
      message: "Internal Server Error",
      error: error instanceof Error ? error.message : undefined,
      status: 500,
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

🚀 Final Thoughts

With this reusable response helper, Next.js 15 API routes become cleaner, more structured, and easier to manage. It simplifies error handling, ensures consistency, and helps in debugging.

Would you use this approach in your Next.js APIs? Let me know your thoughts! 🚀

Comments 5 total

  • Rense Bakker
    Rense BakkerFeb 17, 2025

    Why do you want to know success:true? Seems like redundant information if the http status code already indicates success or failure... You could check if the status code is >= 400 I think?

    • Saiful Islam
      Saiful IslamFeb 17, 2025

      That great. The developer can check the response via the HTTP status code. But sometimes we need a boolean value, Is this a success or not? In my experience, I have faced some issues when working with Flutter developers they want this success value. So I added this success value universal, if developers need this then they can access it as well.

    • Saiful Islam
      Saiful IslamFeb 17, 2025

      Can we connect on Linkedin. Here is my Linkedin account

    • Varun Deva
      Varun DevaFeb 18, 2025

      True
      In some cases i do check response body having any boolean like this just to show success toast or loading state handling etc
      Otherwise its not mandatory :)

      In one way its good to remove redundant information from response body to save the network bandwidth. If your api will get the millions of requests then a small bytes also values more :)

  • pat cummin
    pat cumminFeb 18, 2025

    Whataburger menu features a variety of tasty options, including their famous burgers made with 100% beef patties. You can customize your burger with toppings like cheese, lettuce, and jalapeños. The menu also includes chicken sandwiches, breakfast items, fries, and shakes in different flavors. Whether you're craving a meal or a snack, Whataburger has something for everyone.

Add comment