# Consistent response envelope format

- **Pattern:** `ab-002166` (`saas-api-design.api-consistency.consistent-response-format`)
- **Severity:** medium
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002166
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002166)

## Why it matters

Inconsistent response envelopes — some routes returning `{ data: ... }`, others returning raw objects, others using `{ success: true, result: ... }` — force every API consumer to write bespoke error-handling logic per endpoint. This violates ISO-25010:2011 compatibility.interoperability and means a new integration can silently swallow errors that use the wrong shape. In practice, it produces bugs where a frontend assumes `data` is present but the route returned the payload at root level.

## Severity rationale

Medium because envelope inconsistency causes integration bugs and broken error handling but does not directly enable data exfiltration or service disruption.

## Remediation

Define a shared response helper at `src/lib/api-response.ts` and replace all direct `Response.json()` calls with it:

```ts
// src/lib/api-response.ts
export const apiSuccess = <T>(data: T, status = 200) =>
  Response.json({ data }, { status })

export const apiError = (code: string, message: string, status: number) =>
  Response.json({ error: { code, message } }, { status })
```

Success responses always carry `{ data: <payload> }`; error responses always carry `{ error: { code, message } }`. Import and use these in every route handler. This gives clients a single shape to handle and gives you a single place to add observability later.

## Detection

- **ID:** `consistent-response-format`
- **Severity:** `medium`
- **What to look for:** Enumerate all route handlers and for each classify the JSON response structure returned. Check whether responses follow a consistent envelope pattern. Common patterns: `{ data: ..., error: null }` vs `{ success: true, result: ... }` vs raw data with no wrapper. Also check error responses — do they consistently return `{ error: "message" }` or `{ message: "..." }` or raw strings or HTTP status codes only? Inconsistency means API consumers have to handle multiple shapes.
- **Pass criteria:** At least 80% of route handlers return responses in the same structural shape. Error responses use a consistent shape across routes.
- **Fail criteria:** Handlers return different envelope shapes — some return `{ data: ... }`, others return raw objects, others return `{ success: ..., result: ... }`. Error handling returns different shapes across routes.
- **Skip (N/A) when:** Fewer than 3 API route handlers exist. Signal: 2 or fewer route handler files detected.
- **Detail on fail:** Describe the inconsistency found (e.g., "Auth routes return { user, token } directly; data routes return { data, meta }; error routes return { message } vs { error }"). Max 500 chars.
- **Remediation:** Define a standard response envelope in `src/lib/api-response.ts` and apply it everywhere. A simple, widely-used pattern is: success responses return `{ data: <payload> }` and error responses return `{ error: { code: "ERROR_CODE", message: "Human-readable message" } }`. Create a helper function (e.g., `apiSuccess(data)` and `apiError(code, message, status)`) and replace all direct `Response.json()` calls with it. This makes client-side handling consistent and simplifies error logging.

## External references

- iso-25010 compatibility.interoperability

Taxons: code-quality

HTML version: https://auditbuffet.com/patterns/ab-002166
