DEV Community

Cover image for ๐Ÿš€ Building a Robust API Response Helper in Next.js 15
Saiful Islam
Saiful Islam

Posted on

11

๐Ÿš€ Building a Robust API Response Helper in Next.js 15

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! ๐Ÿš€

Top comments (4)

Collapse
 
brense profile image
Rense Bakker โ€ข

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?

Collapse
 
varundeva profile image
Varun Deva โ€ข

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 :)

Collapse
 
saiful7778 profile image
Saiful Islam โ€ข

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.

Collapse
 
saiful7778 profile image
Saiful Islam โ€ข

Can we connect on Linkedin. Here is my Linkedin account

Some comments may only be visible to logged-in visitors. Sign in to view all comments.

๐Ÿ‘‹ Kindness is contagious

Please leave a โค๏ธ or a friendly comment on this post if you found it helpful!

Okay