ADR 006: API Response Normalization
Status
Accepted
Context
The API currently has inconsistent response formats across different endpoints:
- Some endpoints return data wrapped in
{ data: ... } - Others return data directly at the root level
- Error responses vary between plain text and structured objects
- No standardized metadata like timestamps or request IDs
- Difficult for API consumers to handle responses predictably
Decision
We will implement a standardized response structure for all API responses with the following structure:
Success Response Format
{
"success": true,
"data": <actual_payload>
}
Error Response Format
{
"success": false,
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "Human-readable error message",
"details": { "optional": "context" }
}
}
Standard Error Codes
We define a fixed set of error codes for common scenarios:
VALIDATION_ERROR- Request validation failed (400)UNAUTHORIZED- Authentication required (401)FORBIDDEN- Insufficient permissions (403)RESOURCE_NOT_FOUND- Resource doesn't exist (404)BAD_REQUEST- General client error (400)INTERNAL_ERROR- Server error (500)
Implementation
We provide helper functions in app/helpers/api-response.ts:
// Success response
success(data, { status?, headers? })
// Error response
error(
{ code, message, details? },
{ status?, headers? }
)
// Throw error response
throwError(
{ code, message, details? },
{ status?, headers? }
)
Zod Schema Integration
For OpenAPI documentation, we provide:
createSuccessResponseSchema(dataSchema); // Wraps data schema
ErrorResponseSchema; // Standard error schema
These integrate with the existing OpenAPI registry to auto-generate correct API documentation.
Consequences
Positive
- Predictable API structure - Clients always know where to find data or errors
- Better error handling - Structured error codes for programmatic handling
- Type safety - Zod schemas enforce structure at runtime and compile-time
- OpenAPI compatibility - Automatic documentation generation
- Minimal overhead - Simple structure without unnecessary metadata
- Clear distinction - Success has
data, errors haveerror