> For the complete documentation index, see [llms.txt](https://wisdom.gitbook.io/gyan/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://wisdom.gitbook.io/gyan/core/rest-apis-design/request-response-design.md).

# Request / Response Design

### Request Payload Structure

* Use a structured format like **JSON** or **XML** for request payloads.
* Use camelCase for field names to align with JavaScript conventions.
* Define required fields and optional fields explicitly in API documentation.
* Use `PATCH` for partial updates with only the fields that need to be changed.
* Validate the input request properly
* Use proper validation messages and handle exceptions .

{% hint style="info" %}
See [Bean Validation](/gyan/core/bean-validation.md) for more info.
{% endhint %}

### Response Design

### **Flat Response:**&#x20;

Use for simple resources with minimal metadata.

```json
{
  "id": 1,
  "title": "Product A",
  "price": 100.0
}
```

### **Envelope Response:**&#x20;

Use for more complex responses with metadata, pagination, or additional information.

```java
@Getter
@Builder
public class ApiResponse<T> {
    private String status;           // SUCCESS, ERROR, PARTIAL_SUCCESS
    private String message;          // Human readable message
    private T data;                  // Actual response data
    private List<String> errors;     // List of error messages
    private LocalDateTime timestamp; // Response timestamp
    private String traceId;         // For request tracing
    
    public static <T> ApiResponse<T> success(T data) {
        return ApiResponse.<T>builder()
            .status("SUCCESS")
            .data(data)
            .timestamp(LocalDateTime.now())
            .build();
    }
    
    public static <T> ApiResponse<T> error(String message, List<String> errors) {
        return ApiResponse.<T>builder()
            .status("ERROR")
            .message(message)
            .errors(errors)
            .timestamp(LocalDateTime.now())
            .build();
    }
}
```

### Specific Response Objects

```java
@Getter
@Builder
public class OrderResponse {
    private String orderId;
    private String status;
    private BigDecimal totalAmount;
    private List<OrderItemResponse> items;
    private ShippingDetails shipping;
    private PaymentDetails payment;
    
    @JsonInclude(Include.NON_NULL)
    private List<String> warnings;  // Optional warnings
}

```

### Paginated Response&#x20;

#### **Offset-Based Pagination:**

Use `offset` (starting point) and `limit` (number of items to return).

**Example:**\
`/products?offset=10&limit=20`

```json
{
  "status": "success",
  "data": [
    { "id": 11, "name": "Product 11" },
    { "id": 12, "name": "Product 12" }
  ],
  "meta": {
    "total": 100,
    "offset": 10,
    "limit": 20
  }
}

```

#### **Page-Based Pagination:**

Use `page` and `size`.

**Example Request:**\
`/products?page=2&size=20`

```json
{
  "status": "success",
  "data": [
    { "id": 21, "name": "Product 21" },
    { "id": 22, "name": "Product 22" }
  ],
  "meta": {
    "totalPages": 5,
    "currentPage": 2,
    "pageSize": 20
  }
}
```

#### **Cursor-Based Pagination:**

Use a cursor (e.g., unique ID or timestamp) for fetching the next set of records.

**Example Request:**\
`/products?cursor=abc123&limit=20`

```json
{
  "status": "success",
  "data": [
    { "id": 31, "name": "Product 31" },
    { "id": 32, "name": "Product 32" }
  ],
  "meta": {
    "nextCursor": "xyz456",
    "limit": 20
  }
}
```

#### **Metadata**

Include metadata to provide additional context:

* **total:** Total number of items in the collection.
* **currentPage:** Current page number.
* **totalPages:** Total number of pages.
* **nextCursor:** For cursor-based pagination, the token for the next set of records.

```java
// Sample paginated response
@Getter
@Builder
public class PagedResponse<T> {
    private List<T> data;
    private int pageNumber;
    private int pageSize;
    private long totalElements;
    private int totalPages;
    private boolean hasNext;
    
    private Map<String, Object> metadata;  // For additional info like facets
}
```

### **Error Response Standardization**

Standardizing error responses makes troubleshooting easier for API clients.

* **Code:** Unique error identifier (e.g., HTTP status code or custom code).
* **Message:** Human-readable description of the error.
* **Details:** Additional information, such as invalid fields or suggested fixes (optional).
* **Timestamp:** The time the error occurred (optional).

{% hint style="info" %}
We can use **Problem Detail Standardization** introduced in spring 6
{% endhint %}

```java
@Getter
@Builder
public class ErrorResponse {
    private String code;           // Error code
    private String message;        // User-friendly message
    private String details;        // Technical details (optional)
    private List<FieldError> fieldErrors;  // Validation errors
    private LocalDateTime timestamp;
    
    @Getter
    @Builder
    public static class FieldError {
        private String field;
        private String message;
        private String code;
    }
}

@RestControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(BusinessValidationException.class)
    public ResponseEntity<ErrorResponse> handleValidationException(
            BusinessValidationException ex) {
        
        ErrorResponse error = ErrorResponse.builder()
            .code("VALIDATION_ERROR")
            .message("Validation failed")
            .fieldErrors(ex.getErrors())
            .timestamp(LocalDateTime.now())
            .build();
            
        return ResponseEntity
            .badRequest()
            .body(error);
    }
}

```

Using Problem Detail&#x20;

```java
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ResourceNotFoundException.class)
    public ProblemDetail handleResourceNotFoundException(ResourceNotFoundException ex) {
        ProblemDetail problemDetail = ProblemDetail.forStatus(HttpStatus.NOT_FOUND);
        problemDetail.setTitle("Resource Not Found");
        problemDetail.setDetail(ex.getMessage());
        problemDetail.setProperty("errorCode", "RESOURCE_NOT_FOUND");
        return problemDetail;
    }
}

// response
{
  "type": "about:blank",
  "title": "Resource Not Found",
  "status": 404,
  "detail": "The requested resource was not found.",
  "instance": "/api/users/123",
  "errorCode": "RESOURCE_NOT_FOUND"
}
```

### Async Operation Response

```java
@Getter
@Builder
public class AsyncOperationResponse {
    private String operationId;    // ID to track the operation
    private String status;         // ACCEPTED, IN_PROGRESS, COMPLETED, FAILED
    private String resourceUrl;    // URL to check the status
    private LocalDateTime acceptedAt;
    private LocalDateTime estimatedCompletionTime;
}

@RestController
@RequestMapping("/api/v1/batch-operations")
public class BatchOperationController {
    
    @PostMapping("/orders/import")
    public ResponseEntity<AsyncOperationResponse> importOrders(
            @RequestBody MultipartFile file) {
            
        String operationId = batchService.startImport(file);
        
        AsyncOperationResponse response = AsyncOperationResponse.builder()
            .operationId(operationId)
            .status("ACCEPTED")
            .resourceUrl("/api/v1/batch-operations/" + operationId)
            .acceptedAt(LocalDateTime.now())
            .estimatedCompletionTime(LocalDateTime.now().plusMinutes(5))
            .build();
            
        return ResponseEntity
            .accepted()
            .body(response);
    }
}
```

### Streaming Response

```java
@GetMapping(value = "/orders/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<OrderResponse> streamOrders() {
    return orderService.streamOrders()
        .map(order -> OrderResponse.builder()
            .orderId(order.getId())
            .status(order.getStatus())
            .build());
}
```

### Best Practices Summary:

1. **Request Design**:
   * Use DTOs for request/response objects
   * Implement validation at multiple levels
   * Include tracking information
   * Support idempotency for non-idempotent operations
2. **Response Design**:
   * Use consistent response envelope
   * Include metadata when necessary
   * Support pagination for collections
   * Include relevant links (HATEOAS)
3. **Error Handling**:
   * Consistent error format
   * Appropriate error codes
   * Meaningful error messages
   * Validation error details
4. **Performance**:
   * Use sparse fieldsets
   * Support pagination
   * Consider streaming for large datasets
   * Implement caching headers


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://wisdom.gitbook.io/gyan/core/rest-apis-design/request-response-design.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
