# Alveus API Standard This document describes the standardized API response format used across all Alveus API projects built with `Alveus.Api.Common`. ## Overview All API endpoints return responses wrapped in a standardized `ApiResponse` object. This ensures consistency across all APIs and makes it easier for clients to handle responses, errors, and pagination. ## Response Models ### Base Response: `ApiResponse` The base response model used for all API responses without data payloads. **Properties:** - `success` (boolean): Indicates whether the request was successful - `message` (string): A human-readable message describing the result - `timestamp` (long): When the response was generated (Unix UTC milliseconds since epoch) - `traceId` (string): Request correlation ID for debugging > **Note**: The timestamp is represented as Unix UTC milliseconds since epoch (January 1, 1970). This can be easily converted to a DateTime in most languages: > - **C#**: `DateTimeOffset.FromUnixTimeMilliseconds(timestamp)` > - **JavaScript**: `new Date(timestamp)` > - **Python**: `datetime.fromtimestamp(timestamp / 1000, tz=timezone.utc)` **Example - Success Response:** ```json { "success": true, "message": "Operation completed successfully", "timestamp": 1737033000123, "traceId": "0HN1234567890ABCDEF" } ``` **Example - Simple Failure Response:** ```json { "success": false, "message": "Operation failed", "timestamp": 1737033000123, "traceId": "0HN1234567890ABCDEF" } ``` ### Generic Data Response: `ApiResponse` Used when returning data in the response. Inherits from `ApiResponse`. **Additional Properties:** - `data` (T, optional): The response payload **Example - User Data Response:** ```json { "success": true, "message": "User retrieved successfully", "timestamp": 1737033000123, "traceId": "0HN1234567890ABCDEF", "data": { "id": "550e8400-e29b-41d4-a716-446655440000", "username": "john.doe", "email": "john.doe@example.com", "createdAt": "2025-01-01T00:00:00Z" } } ``` **Example - List of Items:** ```json { "success": true, "message": "Products retrieved successfully", "timestamp": 1737033000123, "traceId": "0HN1234567890ABCDEF", "data": [ { "id": 1, "name": "Product A", "price": 29.99 }, { "id": 2, "name": "Product B", "price": 49.99 } ] } ``` ### Error Response: `ApiErrorResponse` Used when returning validation errors. Inherits from `ApiResponse`. **Additional Properties:** - `errors` (ValidationError[], optional): Array of validation errors **ValidationError Properties:** - `field` (string): The field name that failed validation - `message` (string): The error message - `code` (string, optional): An error code for programmatic handling - `attemptedValue` (any, optional): The value that failed validation **Example - Validation Error Response:** ```json { "success": false, "message": "Validation failed", "timestamp": 1737033000123, "traceId": "0HN1234567890ABCDEF", "errors": [ { "field": "email", "message": "Email address is invalid", "code": "InvalidFormat", "attemptedValue": "not-an-email" }, { "field": "password", "message": "Password must be at least 8 characters", "code": "MinLength", "attemptedValue": "short" } ] } ``` ### Paginated Response: `PagedApiResponse` Used for paginated list endpoints. Inherits from `ApiResponse>`. **Additional Properties:** - `data` (T[]): Array of items for the current page - `pagination` (PaginationMetadata): Pagination information **PaginationMetadata Properties:** - `currentPage` (int): Current page number (1-based) - `pageSize` (int): Number of items per page - `totalCount` (long): Total number of items across all pages - `totalPages` (int): Total number of pages - `hasNextPage` (boolean): Whether a next page exists - `hasPreviousPage` (boolean): Whether a previous page exists **Example - Paginated Users Response:** ```json { "success": true, "message": "Users retrieved successfully", "timestamp": 1737033000123, "traceId": "0HN1234567890ABCDEF", "data": [ { "id": "550e8400-e29b-41d4-a716-446655440000", "username": "john.doe", "email": "john.doe@example.com" }, { "id": "660f9511-f3ac-52e5-b827-557766551111", "username": "jane.smith", "email": "jane.smith@example.com" } ], "pagination": { "currentPage": 1, "pageSize": 2, "totalCount": 10, "totalPages": 5, "hasNextPage": true, "hasPreviousPage": false } } ``` ## HTTP Status Codes The API uses standard HTTP status codes in combination with the `success` field: ### Success Codes (2xx) - **200 OK**: Standard success response - **201 Created**: Resource successfully created - **204 No Content**: Success with no data to return ### Client Error Codes (4xx) - **400 Bad Request**: Invalid request format or parameters - **401 Unauthorized**: Authentication required or failed - **403 Forbidden**: User lacks permission for the requested resource - **404 Not Found**: Resource does not exist - **409 Conflict**: Request conflicts with current state (e.g., duplicate resource) - **422 Unprocessable Entity**: Validation failed ### Server Error Codes (5xx) - **500 Internal Server Error**: Unexpected server error - **503 Service Unavailable**: Service temporarily unavailable ## Common Response Patterns ### Success with Data (200 OK) ```json { "success": true, "message": "Resource retrieved successfully", "timestamp": 1737033000123, "traceId": "0HN1234567890ABCDEF", "data": { "id": 123, "name": "Example" } } ``` ### Resource Created (201 Created) ```json { "success": true, "message": "Resource created", "timestamp": 1737033000123, "traceId": "0HN1234567890ABCDEF", "data": { "id": 124, "name": "New Resource", "createdAt": "2026-01-16T14:30:00Z" } } ``` ### Success Without Data (204 No Content) ```json { "success": true, "message": "No content", "timestamp": 1737033000123, "traceId": "0HN1234567890ABCDEF" } ``` ### Bad Request (400) ```json { "success": false, "message": "Bad request", "timestamp": 1737033000123, "traceId": "0HN1234567890ABCDEF" } ``` ### Unauthorized (401) ```json { "success": false, "message": "Unauthorized", "timestamp": 1737033000123, "traceId": "0HN1234567890ABCDEF" } ``` ### Forbidden (403) ```json { "success": false, "message": "Forbidden", "timestamp": 1737033000123, "traceId": "0HN1234567890ABCDEF" } ``` ### Not Found (404) ```json { "success": false, "message": "Resource not found", "timestamp": 1737033000123, "traceId": "0HN1234567890ABCDEF" } ``` ### Conflict (409) ```json { "success": false, "message": "Resource already exists", "timestamp": 1737033000123, "traceId": "0HN1234567890ABCDEF" } ``` ### Validation Error (422) ```json { "success": false, "message": "Validation failed", "timestamp": "2026-01-16T14:30:00.123Z", "traceId": "0HN1234567890ABCDEF", "errors": [ { "field": "username", "message": "Username is required", "code": "Required" } ] } ``` ### Internal Server Error (500) ```json { "success": false, "message": "An unexpected error occurred", "timestamp": 1737033000123, "traceId": "0HN1234567890ABCDEF" } ``` ## Using the API Standard in Controllers Controllers should inherit from `ApiControllerBase` which provides helper methods for returning standardized responses: ### Success Methods ```csharp // Success with data return Success("User retrieved", user); // Success with data and custom status code return Success("User updated", user, 200); // Success without data return Success("Operation completed"); // Created (201) return Created("User created", newUser); return Created(newUser); // Uses default message // No Content (204) return NoContent(); ``` ### Error Methods ```csharp // Generic failure return Failure("Something went wrong"); // Bad Request (400) return BadRequest("Invalid input"); // Unauthorized (401) return Unauthorized("Authentication required"); // Forbidden (403) return Forbidden("Access denied"); // Not Found (404) return NotFound("User not found"); // Conflict (409) return Conflict("Username already exists"); ``` ### Pagination Methods ```csharp // Paginated response return PagedSuccess( "Users retrieved", users, currentPage: 1, pageSize: 10, totalCount: 100 ); ``` ## Exception Handling When using the `Alveus.Api.Common.Client` for making API calls, responses are automatically parsed and exceptions are thrown for error responses: ### Built-in Exceptions All exceptions inherit from `ApiException` which contains: - `Message`: Error description - `StatusCode`: HTTP status code **Available Exception Types:** - `BadRequestException` (400) - `UnauthorizedException` (401) - `ForbiddenException` (403) - `NotFoundException` (404) - `ConflictException` (409) - `ValidationException` (422) - includes `ValidationErrors` property - `TooManyRequestsException` (429) - `InternalServerErrorException` (500) - `ServiceUnavailableException` (503) **Example Usage:** ```csharp try { var user = await apiClient.GetUserAsync(userId); } catch (NotFoundException ex) { Console.WriteLine($"User not found: {ex.Message}"); } catch (ValidationException ex) { foreach (var error in ex.ValidationErrors) { Console.WriteLine($"{error.Field}: {error.Message}"); } } catch (ApiException ex) { Console.WriteLine($"API Error ({ex.StatusCode}): {ex.Message}"); } ``` ## Best Practices 1. **Always wrap responses**: Use the helper methods from `ApiControllerBase` to ensure consistent response format 2. **Include meaningful messages**: Provide clear, actionable messages for both success and error cases 3. **Use appropriate status codes**: Match the HTTP status code to the actual result 4. **Validate input**: Return `ValidationException` with detailed field-level errors for invalid input 5. **Handle exceptions**: Use try-catch blocks and return appropriate error responses 6. **Document your endpoints**: Include response examples in API documentation 7. **Use pagination**: For list endpoints that may return many items, use `PagedSuccess` 8. **Be consistent**: Always use the same response structure across all endpoints 9. **Leverage traceId for debugging**: Use the traceId to correlate requests across services and logs ## Complete API Endpoint Example ```csharp [HttpGet("users/{id}")] [ProducesResponseType(typeof(ApiResponse), 200)] [ProducesResponseType(typeof(ApiResponse), 404)] public async Task GetUser(string id) { var user = await _userService.GetUserByIdAsync(id); if (user == null) { return NotFound($"User with ID '{id}' not found"); } return Success("User retrieved successfully", user); } [HttpPost("users")] [ProducesResponseType(typeof(ApiResponse), 201)] [ProducesResponseType(typeof(ApiErrorResponse), 422)] public async Task CreateUser([FromBody] CreateUserRequest request) { if (!ModelState.IsValid) { var errors = ModelState .Where(x => x.Value?.Errors.Count > 0) .SelectMany(x => x.Value!.Errors.Select(e => new ValidationError { Field = x.Key, Message = e.ErrorMessage })); return Failure("Validation failed", errors, 422); } var user = await _userService.CreateUserAsync(request); return Created("User created successfully", user); } [HttpGet("users")] [ProducesResponseType(typeof(PagedApiResponse), 200)] public async Task GetUsers( [FromQuery] int page = 1, [FromQuery] int pageSize = 10) { var (users, totalCount) = await _userService.GetUsersAsync(page, pageSize); return PagedSuccess( "Users retrieved successfully", users, page, pageSize, totalCount ); } ``` ## Summary This standardized response format provides: - **Consistency**: All endpoints return responses in the same structure - **Type Safety**: Strong typing with generics for data payloads - **Easy Error Handling**: Automatic exception throwing on client side - **Developer Experience**: Clear, predictable responses with helpful metadata - **Debugging**: TraceId for correlating requests across services - **Pagination Support**: Built-in pagination metadata for list endpoints - **Validation**: Structured validation error reporting - **AOT Compatibility**: Works with Native AOT compilation in .NET 8+