# ADR-028: API Response Standardization and Envelope Pattern **Date**: 2026-01-09 **Status**: Proposed ## Context The API currently has inconsistent response formats across different endpoints: 1. Some endpoints return raw data arrays (`[{...}, {...}]`) 2. Some return wrapped objects (`{ data: [...] }`) 3. Pagination is handled inconsistently (some use `page`/`limit`, others use `offset`/`count`) 4. Error responses vary in structure between middleware and route handlers 5. No standard for including metadata (pagination info, request timing, etc.) This inconsistency creates friction for: - Frontend developers who must handle multiple response formats - API documentation and client SDK generation - Implementing consistent error handling across the application - Future API versioning transitions ## Decision We will adopt a standardized response envelope pattern for all API responses. ### Success Response Format ```typescript interface ApiSuccessResponse { success: true; data: T; meta?: { // Pagination (when applicable) pagination?: { page: number; limit: number; total: number; totalPages: number; hasNextPage: boolean; hasPrevPage: boolean; }; // Timing requestId?: string; timestamp?: string; duration?: number; }; } ``` ### Error Response Format ```typescript interface ApiErrorResponse { success: false; error: { code: string; // Machine-readable error code (e.g., 'VALIDATION_ERROR') message: string; // Human-readable message details?: unknown; // Additional context (validation errors, etc.) }; meta?: { requestId?: string; timestamp?: string; }; } ``` ### Implementation Approach 1. **Response Helper Functions**: Create utility functions in `src/utils/apiResponse.ts`: - `sendSuccess(res, data, meta?)` - `sendPaginated(res, data, pagination)` - `sendError(res, code, message, details?, statusCode?)` 2. **Error Handler Integration**: Update `errorHandler.ts` to use the standard error format 3. **Gradual Migration**: Apply to new endpoints immediately, migrate existing endpoints incrementally 4. **TypeScript Types**: Export response types for frontend consumption ## Consequences ### Positive - **Consistency**: All responses follow a predictable structure - **Type Safety**: Frontend can rely on consistent types - **Debugging**: Request IDs and timestamps aid in issue investigation - **Pagination**: Standardized pagination metadata reduces frontend complexity - **API Evolution**: Envelope pattern makes it easier to add fields without breaking changes ### Negative - **Verbosity**: Responses are slightly larger due to envelope overhead - **Migration Effort**: Existing endpoints need updating - **Learning Curve**: Developers must learn and use the helper functions ## Implementation Status ### What's Implemented - ❌ Not yet implemented ### What Needs To Be Done 1. Create `src/utils/apiResponse.ts` with helper functions 2. Create `src/types/api.ts` with response type definitions 3. Update `errorHandler.ts` to use standard error format 4. Create migration guide for existing endpoints 5. Update 2-3 routes as examples 6. Document pattern in this ADR ## Example Usage ```typescript // In a route handler router.get('/flyers', async (req, res, next) => { try { const { page = 1, limit = 20 } = req.query; const { flyers, total } = await flyerService.getFlyers({ page, limit }); return sendPaginated(res, flyers, { page, limit, total, }); } catch (error) { next(error); } }); // Response: // { // "success": true, // "data": [...], // "meta": { // "pagination": { // "page": 1, // "limit": 20, // "total": 150, // "totalPages": 8, // "hasNextPage": true, // "hasPrevPage": false // }, // "requestId": "abc-123", // "timestamp": "2026-01-09T12:00:00.000Z" // } // } ```