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 successfulmessage(string): A human-readable message describing the resulttimestamp(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:
{
"success": true,
"message": "Operation completed successfully",
"timestamp": 1737033000123,
"traceId": "0HN1234567890ABCDEF"
}
Example - Simple Failure Response:
{
"success": false,
"message": "Operation failed",
"timestamp": 1737033000123,
"traceId": "0HN1234567890ABCDEF"
}
Generic Data Response: ApiResponse<T>
Used when returning data in the response. Inherits from ApiResponse.
Additional Properties:
data(T, optional): The response payload
Example - User Data Response:
{
"success": true,
"message": "User retrieved successfully",
"timestamp": 1737033000123,
"traceId": "0HN1234567890ABCDEF",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"username": "john.doe",
"email": "[email protected]",
"createdAt": "2025-01-01T00:00:00Z"
}
}
Example - List of Items:
{
"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 validationmessage(string): The error messagecode(string, optional): An error code for programmatic handlingattemptedValue(any, optional): The value that failed validation
Example - Validation Error Response:
{
"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<T>
Used for paginated list endpoints. Inherits from ApiResponse<IEnumerable<T>>.
Additional Properties:
data(T[]): Array of items for the current pagepagination(PaginationMetadata): Pagination information
PaginationMetadata Properties:
currentPage(int): Current page number (1-based)pageSize(int): Number of items per pagetotalCount(long): Total number of items across all pagestotalPages(int): Total number of pageshasNextPage(boolean): Whether a next page existshasPreviousPage(boolean): Whether a previous page exists
Example - Paginated Users Response:
{
"success": true,
"message": "Users retrieved successfully",
"timestamp": 1737033000123,
"traceId": "0HN1234567890ABCDEF",
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"username": "john.doe",
"email": "[email protected]"
},
{
"id": "660f9511-f3ac-52e5-b827-557766551111",
"username": "jane.smith",
"email": "[email protected]"
}
],
"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)
{
"success": true,
"message": "Resource retrieved successfully",
"timestamp": 1737033000123,
"traceId": "0HN1234567890ABCDEF",
"data": {
"id": 123,
"name": "Example"
}
}
Resource Created (201 Created)
{
"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)
{
"success": true,
"message": "No content",
"timestamp": 1737033000123,
"traceId": "0HN1234567890ABCDEF"
}
Bad Request (400)
{
"success": false,
"message": "Bad request",
"timestamp": 1737033000123,
"traceId": "0HN1234567890ABCDEF"
}
Unauthorized (401)
{
"success": false,
"message": "Unauthorized",
"timestamp": 1737033000123,
"traceId": "0HN1234567890ABCDEF"
}
Forbidden (403)
{
"success": false,
"message": "Forbidden",
"timestamp": 1737033000123,
"traceId": "0HN1234567890ABCDEF"
}
Not Found (404)
{
"success": false,
"message": "Resource not found",
"timestamp": 1737033000123,
"traceId": "0HN1234567890ABCDEF"
}
Conflict (409)
{
"success": false,
"message": "Resource already exists",
"timestamp": 1737033000123,
"traceId": "0HN1234567890ABCDEF"
}
Validation Error (422)
{
"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)
{
"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
// 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
// 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
// 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 descriptionStatusCode: HTTP status code
Available Exception Types:
BadRequestException(400)UnauthorizedException(401)ForbiddenException(403)NotFoundException(404)ConflictException(409)ValidationException(422) - includesValidationErrorspropertyTooManyRequestsException(429)InternalServerErrorException(500)ServiceUnavailableException(503)
Example Usage:
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
- Always wrap responses: Use the helper methods from
ApiControllerBaseto ensure consistent response format - Include meaningful messages: Provide clear, actionable messages for both success and error cases
- Use appropriate status codes: Match the HTTP status code to the actual result
- Validate input: Return
ValidationExceptionwith detailed field-level errors for invalid input - Handle exceptions: Use try-catch blocks and return appropriate error responses
- Document your endpoints: Include response examples in API documentation
- Use pagination: For list endpoints that may return many items, use
PagedSuccess - Be consistent: Always use the same response structure across all endpoints
- Leverage traceId for debugging: Use the traceId to correlate requests across services and logs
Complete API Endpoint Example
[HttpGet("users/{id}")]
[ProducesResponseType(typeof(ApiResponse<UserDto>), 200)]
[ProducesResponseType(typeof(ApiResponse), 404)]
public async Task<IActionResult> 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<UserDto>), 201)]
[ProducesResponseType(typeof(ApiErrorResponse), 422)]
public async Task<IActionResult> 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<UserDto>), 200)]
public async Task<IActionResult> 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+