← All posts
engineeringdx

Why every endpoint returns the same response envelope, even error responses

Mahesh Naidu·4 min read

Every single endpoint across every namespace — finance, legal, weather, numerology, whatever — calls one of exactly two functions to format its response: apiSuccess or apiError. No endpoint writes its own response shape. That constraint is small but it's the single biggest lever on developer experience across the platform.

The two functions

export function apiSuccess<T>(data: T, meta?: Record<string, unknown>) {
  return NextResponse.json({ success: true, data, ...(meta && { meta }) });
}

export function apiError(message: string, status: number) {
  return NextResponse.json(
    { error: true, message, status, docs: "https://aplicious.com/docs" },
    { status }
  );
}

That's the entire envelope. Success looks like { success: true, data: {...} }, with an optional meta field for things like pagination. Failure looks like { error: true, message: '...', status: 400, docs: 'https://aplicious.com/docs' }. Every route imports both from one shared module and never constructs a raw NextResponse.json(...) by hand for its actual payload.

What this buys you as a consumer

You write exactly one response handler in your client code, and it works for all 400+ endpoints:

async function callApi(url: string) {
  const res = await fetch(url, { headers: { "X-API-Key": apiKey } });
  const json = await res.json();
  if (json.error) throw new Error(json.message);
  return json.data;
}

You never need a per-endpoint adapter. You never need to check whether this particular namespace wraps its data in results versus data versus nothing at all. The error path always includes a docs link pointing back to the documentation, so a confusing error message is never a dead end.

The discipline this requires

The hard part isn't writing these two functions once — it's the discipline of never bypassing them, even when it would be marginally faster to return something custom for one specific edge case. We treat any endpoint that doesn't go through apiSuccess/apiErroras a bug, not a style preference, because the value of a consistent envelope drops fast the moment there's a single exception a developer has to remember.

Try APlicious free
One key. 34 live namespaces. 500 free calls per month — no credit card required.
Get your free API key →
← Back to all posts