Blame
| 89442e | Melisha Dsouza | 2026-01-14 15:26:56 | 1 | # **Response/Request structure guidelines** |
| 2 | ||||
| 3 | ## **Introduction** |
|||
| 4 | ||||
| 5 | This document defines the Request/Response structure specifically for projects using a service-oriented architecture (SOA). |
|||
| 606e69 | Melisha Dsouza | 2026-01-16 10:07:56 | 6 | |
| 89442e | Melisha Dsouza | 2026-01-14 15:26:56 | 7 | For projects built on other architectural styles (e.g., MSA), the structure may vary, although the general principles remain consistent. |
| 606e69 | Melisha Dsouza | 2026-01-16 10:07:56 | 8 | |
| 89442e | Melisha Dsouza | 2026-01-14 15:26:56 | 9 | The purpose of this document is to establish a unified, standardized approach to organizing requests and responses across Enovate projects. This standardization aims to ensure consistency, enhance maintainability, and support predictable integration patterns across teams and services. |
| 606e69 | Melisha Dsouza | 2026-01-16 10:07:56 | 10 | |
| 89442e | Melisha Dsouza | 2026-01-14 15:26:56 | 11 | Although some examples may resemble structures used in existing services, the guidelines presented here are not bound to any particular project. |
| 12 | ||||
| 13 | ## **Objectives** |
|||
| 14 | ||||
| 15 | 1. Establish a unified structure for incoming requests and outgoing responses. |
|||
| 16 | 2. Improve code readability, maintainability, and long-term project scalability. |
|||
| 17 | 3. Ensure that all developers follow consistent patterns when designing endpoints. |
|||
| 18 | 4. Support clear API behaviour, predictable error handling, and forward compatibility. |
|||
| 19 | ||||
| 20 | ## **General principles** |
|||
| 21 | ||||
| 22 | #### **1. Consistency across all services** |
|||
| 23 | ||||
| 24 | Every API should follow the same structural approach for request and response formatting. This helps ensure: |
|||
| 25 | * Predictable behaviour for consumers, |
|||
| 26 | * Simpler onboarding for new developers. |
|||
| 27 | ||||
| 28 | #### **2. Clear separation of concerns** |
|||
| 29 | ||||
| 30 | Requests should focus only on the required input data. Responses should focus only on returned values and execution results. No business logic should leak into either. |
|||
| 31 | ||||
| 32 | #### **3. Strong typing and validation** |
|||
| 33 | ||||
| 34 | Each request must be validated, and each response must maintain a predictable and strict schema. This prevents runtime inconsistencies and unexpected behaviours. |
|||
| 35 | ||||
| 36 | > ### **Request structure** |
|||
| 37 | ||||
| 38 | Requests across all services should follow a clearly defined pattern. |
|||
| 39 | ||||
| 40 | #### **1. Header (mandatory)** |
|||
| 41 | ||||
| 42 | Every request must include a header object containing metadata about the request. |
|||
| 43 | Fields: |
|||
| 44 | 1. `id` - unique request identifier. |
|||
| 45 | 2. `version` - API version. |
|||
| 46 | 3. `service` - Name of the service handling the request. |
|||
| 47 | 4. `method` - The method being invoked. |
|||
| 48 | 5. `token` - Authentication key. |
|||
| 49 | ||||
| 50 | **Example:** |
|||
| 51 | ||||
| 52 | ``` |
|||
| 53 | { |
|||
| 54 | "header":{ |
|||
| 55 | "id":"1b7665f09cf71be154cf9", |
|||
| 56 | "version":"0.1.1", |
|||
| 57 | "service":"service-name", |
|||
| 58 | "method":"methodName", |
|||
| 59 | "token":"88b6d9105883dd5235ae1b7665f09cf71be154cf9508304284b13e84aba9669c07fce8f45e3dffd944b6217f9f2db4fcf3fe24bd6b5db703683996f56b792c07" |
|||
| 60 | } |
|||
| 61 | } |
|||
| 62 | ``` |
|||
| 63 | ||||
| 64 | #### **2. Data (mandatory)** |
|||
| 65 | ||||
| 66 | This is the main payload of the request. Every endpoint must define a clear schema for the data object. |
|||
| 67 | ||||
| 68 | **Example**: |
|||
| 69 | ||||
| 70 | ``` |
|||
| 71 | { |
|||
| 72 | "data": { |
|||
| 73 | "email": "user@example.com", |
|||
| 74 | "password": "password123" |
|||
| 75 | } |
|||
| 76 | } |
|||
| 77 | ``` |
|||
| 78 | ||||
| 79 | #### **3. Metadata (optional)** |
|||
| 80 | ||||
| 81 | Used when additional context is needed, language preferences for example. |
|||
| 82 | ||||
| 83 | **Example**: |
|||
| 84 | ||||
| 85 | ``` |
|||
| 86 | { |
|||
| 87 | "meta": { |
|||
| 88 | "locale": "en-US", |
|||
| 89 | } |
|||
| 90 | } |
|||
| 91 | ``` |
|||
| 92 | ||||
| 93 | #### **Validation rules** |
|||
| 94 | ||||
| 95 | All incoming requests must be validated using server-side validation. The rules should be tightly defined and should reject malformed or incomplete data. This is achieved with the help of: |
|||
| 96 | 1. Centralising request validation logic. |
|||
| 97 | 2. Providing a uniform validation error response. |
|||
| 98 | 3. Implementing JSON schema-based validations for consistency. |
|||
| 99 | ||||
| 100 | > ### **Response structure** |
|||
| 101 | ||||
| 102 | Every response - successful or erroneous - should follow a consistent structure. |
|||
| 103 | ||||
| 104 | #### **1. Success response structure** |
|||
| 105 | ||||
| 106 | **Base format:** |
|||
| 107 | ||||
| 108 | ``` |
|||
| 109 | { |
|||
| 110 | "success": true, |
|||
| 111 | "data": {}, |
|||
| 112 | "meta": {} |
|||
| 113 | } |
|||
| 114 | ``` |
|||
| 115 | ||||
| 116 | **Fields:** |
|||
| 117 | ||||
| 118 | 1. `success` - always a boolean. Always true for successful responses. |
|||
| 119 | 2. `data` - the actual payload returned by the system. Should never be null unless explicitly required. |
|||
| 120 | 3. `meta` - optional additional information (processing time, environment flags, etc.). |
|||
| 121 | ||||
| 122 | **Example**: |
|||
| 123 | ||||
| 124 | ``` |
|||
| 125 | { |
|||
| 126 | "success": true, |
|||
| 127 | "data": { |
|||
| 128 | "id": "e501c2bf", |
|||
| 129 | "status": "created" |
|||
| 130 | }, |
|||
| 131 | "meta": { |
|||
| 132 | "timestamp": "2025-02-10T12:45:00Z" |
|||
| 133 | } |
|||
| 134 | } |
|||
| 135 | ``` |
|||
| 136 | #### **2. Error response structure** |
|||
| 137 | ||||
| 138 | Error responses must follow the same pattern as success responses, with predictable fields. |
|||
| 139 | ||||
| 140 | **Base format:** |
|||
| 141 | ||||
| 142 | ``` |
|||
| 143 | { |
|||
| 144 | "success": false, |
|||
| 145 | "error": { |
|||
| 146 | "code": "string", |
|||
| 147 | "message": "string", |
|||
| 148 | "details": {} |
|||
| 149 | }, |
|||
| 150 | "meta": {} |
|||
| 151 | } |
|||
| 152 | ``` |
|||
| 153 | ||||
| 154 | **Fields**: |
|||
| 155 | ||||
| 156 | 1. `success` - always false. |
|||
| 157 | 2. `error.code` - a short, consistent identifier (e.g., VALIDATION_ERROR, NOT_FOUND, UNAUTHORIZED). |
|||
| 158 | 3. `error.message` - human-readable explanation. |
|||
| 159 | 4. `error.details` - optional field containing validation details or additional context. |
|||
| 160 | 5. `meta` - same use as in success responses. |
|||
| 161 | ||||
| 162 | **Example**: |
|||
| 163 | ||||
| 164 | ``` |
|||
| 165 | { |
|||
| 166 | "success": false, |
|||
| 167 | "error": { |
|||
| 168 | "code": "VALIDATION_ERROR", |
|||
| 169 | "message": "The provided data is invalid.", |
|||
| 170 | "details": { |
|||
| 171 | "email": "Invalid email format" |
|||
| 172 | } |
|||
| 173 | }, |
|||
| 174 | "meta": { |
|||
| 175 | "timestamp": "2025-02-10T12:45:00Z" |
|||
| 176 | } |
|||
| 177 | } |
|||
| 178 | ``` |
|||
| 179 | Each project must have a unified list of error codes with descriptions to ensure consistent usage across all services. |
|||
| 180 | ||||
| 181 | ### **Standardised pagination, sorting, and date filtering schemas** |
|||
| 182 | ||||
| 183 | To ensure uniform behaviour across all services, projects should use standardised schemas for pagination, sorting, and date-based filtering. These schemas are reusable, type-safe, and prevent inconsistencies between endpoints. |
|||
| 184 | ||||
| 185 | **Pagination schema:** |
|||
| 186 | ||||
| 187 | ``` |
|||
| 188 | export const paginationSchema = { |
|||
| 189 | type: "object", |
|||
| 190 | description: "Pagination parameters in API requests.", |
|||
| 191 | properties: { |
|||
| 192 | page: { |
|||
| 193 | type: "integer", |
|||
| 194 | minimum: 1, |
|||
| 195 | default: 1, |
|||
| 196 | description: "To calculate the starting index (offset) for the data query.", |
|||
| 197 | }, |
|||
| 198 | pageSize: { |
|||
| 199 | type: "integer", |
|||
| 200 | minimum: 1, |
|||
| 201 | maximum: 100, |
|||
| 202 | default: 20, |
|||
| 203 | description: "The number of items to return.", |
|||
| 204 | }, |
|||
| 205 | }, |
|||
| 206 | required: ["page", "pageSize"], |
|||
| 207 | additionalProperties: false, |
|||
| 208 | }; |
|||
| 209 | ``` |
|||
| 210 | **Sorting schema:** |
|||
| 211 | ||||
| 212 | ``` |
|||
| 213 | export const sortSchema = { |
|||
| 214 | type: "object", |
|||
| 215 | description: "Sorting parameters in API requests.", |
|||
| 216 | properties: { |
|||
| 217 | field: { |
|||
| 218 | type: "string", |
|||
| 219 | minLength: 1, |
|||
| 220 | maxLength: 32, |
|||
| 221 | description: "The field name to sort by.", |
|||
| 222 | default: "createdAt", |
|||
| 223 | }, |
|||
| 224 | direction: { |
|||
| 225 | type: "string", |
|||
| 226 | enum: ["asc", "desc"], |
|||
| 227 | default: "desc", |
|||
| 228 | description: "The sort direction.", |
|||
| 229 | }, |
|||
| 230 | }, |
|||
| 231 | required: ["field", "direction"], |
|||
| 232 | additionalProperties: false, |
|||
| 233 | }; |
|||
| 234 | ``` |
|||
| 235 | ||||
| 236 | **Date filter schema:** |
|||
| 237 | ``` |
|||
| 238 | export const dateFilterSchema = { |
|||
| 239 | type: "object", |
|||
| 240 | description: "Filter by dates (period)", |
|||
| 241 | properties: { |
|||
| 242 | dateFrom: { type: "string", format: "date-time" }, |
|||
| 243 | dateTo: { type: "string", format: "date-time" }, |
|||
| 244 | }, |
|||
| 245 | additionalProperties: false, |
|||
| 246 | }; |
|||
| 247 | ``` |
|||
| 248 | Use ISO-8601 for all timestamps. |
|||
| 249 | ||||
| 250 | #### **Pagination structure** |
|||
| 251 | ||||
| 252 | For all endpoints returning collections, the response should include a standard pagination structure. |
|||
| 253 | ||||
| 254 | **Example:** |
|||
| 255 | ``` |
|||
| 256 | { |
|||
| 257 | "success": true, |
|||
| 258 | "data": { |
|||
| 259 | "items": [ |
|||
| 260 | { "id": 1 }, |
|||
| 261 | { "id": 2 } |
|||
| 262 | ], |
|||
| 263 | "page": 1, |
|||
| 264 | "pageSize": 25, |
|||
| 265 | "total": 48, |
|||
| 266 | "totalPages": 2 |
|||
| 267 | }, |
|||
| 268 | "meta": {} |
|||
| 269 | } |
|||
| 270 | ||||
| 271 | ``` |
|||
| 272 | ||||
| 273 | #### **Naming and structural conventions** |
|||
| 274 | ||||
| 275 | 1. All keys must follow camelCase formatting. |
|||
| 276 | 2. Limit nesting depth wherever possible. |
|||
| 277 | 3. Avoid mixing unrelated concerns in a single response. |
|||
| 278 | 4. Maintain strict typing across the entire structure. |
|||
| 279 | ||||
| 280 | ### **Summary** |
|||
| 281 | ||||
| 282 | By following the structure outlined in this document, all teams within Enovate will benefit from predictable API behavior, improved developer experience, and easier long-term maintenance. These guidelines are intended to evolve, and contributions or suggestions for improvement are encouraged. |
|||
| 606e69 | Melisha Dsouza | 2026-01-16 10:07:56 | 283 | |
| 284 | **Document** - [RRP](https://docs.google.com/document/d/16OOwKinohRJFUG9E2un1HEXaQyvrGPM0qoYhaFi3jGA/edit?usp=sharing) |