DEV Community

Er. Bhupendra
Er. Bhupendra

Posted on

PART 4 :CONTROLLER ALL CONCEPT IN SPRINGBOOT PROJECT

❌ NAHI! ApiResponse Inbuilt NAHI Hai


ApiResponse Khud Banana Padta Hai 🛠️

// Ye aapko KHUD banana hota hai - Standard industry practice
package com.company.ecommerce.dto.response;

import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.*;

import java.util.List;

/**
 * Standard API Response Wrapper
 * Ye class aapko khud banani padegi
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)  // null fields skip karenge
public class ApiResponse<T> {

    private Boolean success;
    private String message;
    private T data;
    private List<String> errors;
    private Long timestamp;
    private String path;

    // Helper static methods
    public static <T> ApiResponse<T> success(T data) {
        return ApiResponse.<T>builder()
                .success(true)
                .data(data)
                .timestamp(System.currentTimeMillis())
                .build();
    }

    public static <T> ApiResponse<T> success(String message, T data) {
        return ApiResponse.<T>builder()
                .success(true)
                .message(message)
                .data(data)
                .timestamp(System.currentTimeMillis())
                .build();
    }

    public static <T> ApiResponse<T> error(String message) {
        return ApiResponse.<T>builder()
                .success(false)
                .message(message)
                .timestamp(System.currentTimeMillis())
                .build();
    }

    public static <T> ApiResponse<T> error(String message, List<String> errors) {
        return ApiResponse.<T>builder()
                .success(false)
                .message(message)
                .errors(errors)
                .timestamp(System.currentTimeMillis())
                .build();
    }
}
Enter fullscreen mode Exit fullscreen mode

Kyu Banana Padta Hai?

Benefits:

1. CONSISTENCY
   Sab endpoints same format mein response denge

2. ERROR HANDLING
   Success/failure easily differentiate kar sakte

3. METADATA
   Timestamp, pagination, etc add kar sakte

4. CLIENT EASY
   Frontend developers ko parsing easy hogi
Enter fullscreen mode Exit fullscreen mode

Response Format Examples

1. Success Response:

{
  "success": true,
  "message": "Product created successfully",
  "data": {
    "id": 123,
    "name": "Laptop",
    "price": 50000
  },
  "timestamp": 1709388000000
}
Enter fullscreen mode Exit fullscreen mode

2. Error Response:

{
  "success": false,
  "message": "Validation failed",
  "errors": [
    "Name is required",
    "Price must be greater than 0"
  ],
  "timestamp": 1709388000000,
  "path": "/api/products"
}
Enter fullscreen mode Exit fullscreen mode

3. List Response:

{
  "success": true,
  "data": [
    {"id": 1, "name": "Product 1"},
    {"id": 2, "name": "Product 2"}
  ],
  "timestamp": 1709388000000
}
Enter fullscreen mode Exit fullscreen mode

Controller Mein Kaise Use Kare

@RestController
@RequestMapping("/api/products")
public class ProductController {

    // Success case
    @PostMapping
    public ResponseEntity<ApiResponse<ProductResponse>> create(
            @RequestBody ProductRequest request
    ) {
        Product product = productService.create(request);

        return ResponseEntity
                .status(HttpStatus.CREATED)
                .body(ApiResponse.success(
                    "Product created successfully", 
                    productMapper.toResponse(product)
                ));
    }

    // List response
    @GetMapping
    public ResponseEntity<ApiResponse<List<ProductResponse>>> getAll() {
        List<Product> products = productService.findAll();

        return ResponseEntity.ok(
            ApiResponse.success(productMapper.toResponseList(products))
        );
    }

    // Error will be handled by GlobalExceptionHandler
}
Enter fullscreen mode Exit fullscreen mode

Alternative: Spring Boot Inbuilt ResponseEntity

// Agar ApiResponse nahi banana chahte, to sirf ResponseEntity use kar sakte ho

@GetMapping("/{id}")
public ResponseEntity<ProductResponse> getProduct(@PathVariable Long id) {
    Product product = productService.findById(id);
    return ResponseEntity.ok(productMapper.toResponse(product));
}

// Response:
{
  "id": 123,
  "name": "Laptop",
  "price": 50000
}

// But yahan consistency nahi hai
// Kabhi direct object, kabhi error format different
Enter fullscreen mode Exit fullscreen mode

Industry Standard Wrapper Variations

Option 1: Simple Wrapper

@Data
@AllArgsConstructor
public class ApiResponse<T> {
    private boolean success;
    private T data;
    private String message;
}
Enter fullscreen mode Exit fullscreen mode

Option 2: Detailed Wrapper (Recommended)

@Data
@Builder
public class ApiResponse<T> {
    private Boolean success;
    private String message;
    private T data;
    private List<String> errors;
    private Integer statusCode;
    private Long timestamp;
    private String path;
    private String requestId;  // For tracing
}
Enter fullscreen mode Exit fullscreen mode

Option 3: Separate Success/Error Classes

// Success Response
@Data
@AllArgsConstructor
public class SuccessResponse<T> {
    private T data;
    private String message;
    private Long timestamp;
}

// Error Response
@Data
@AllArgsConstructor
public class ErrorResponse {
    private String message;
    private List<String> errors;
    private Integer statusCode;
    private String path;
    private Long timestamp;
}
Enter fullscreen mode Exit fullscreen mode

Companies Mein Kaisa Use Hota Hai

Company 1: Flipkart Style

{
  "status": "success",
  "code": 200,
  "data": { ... },
  "meta": {
    "timestamp": "2024-02-02T10:30:00Z",
    "requestId": "abc-123-xyz"
  }
}
Enter fullscreen mode Exit fullscreen mode

Company 2: Amazon Style

{
  "result": { ... },
  "metadata": {
    "statusCode": 200,
    "message": "Success"
  }
}
Enter fullscreen mode Exit fullscreen mode

Company 3: Google Style

{
  "data": { ... },
  "error": null
}
Enter fullscreen mode Exit fullscreen mode

Complete Example with GlobalExceptionHandler

// ApiResponse class
@Data
@Builder
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ApiResponse<T> {
    private Boolean success;
    private String message;
    private T data;
    private List<String> errors;
    private Long timestamp;
    private String path;

    public static <T> ApiResponse<T> success(T data) {
        return ApiResponse.<T>builder()
                .success(true)
                .data(data)
                .timestamp(System.currentTimeMillis())
                .build();
    }

    public static <T> ApiResponse<T> error(String message, String path) {
        return ApiResponse.<T>builder()
                .success(false)
                .message(message)
                .timestamp(System.currentTimeMillis())
                .path(path)
                .build();
    }
}

// GlobalExceptionHandler
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ResourceNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ApiResponse<Void> handleNotFound(
            ResourceNotFoundException ex,
            HttpServletRequest request
    ) {
        return ApiResponse.error(ex.getMessage(), request.getRequestURI());
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ApiResponse<Void> handleValidation(
            MethodArgumentNotValidException ex,
            HttpServletRequest request
    ) {
        List<String> errors = ex.getBindingResult()
                .getFieldErrors()
                .stream()
                .map(error -> error.getField() + ": " + error.getDefaultMessage())
                .collect(Collectors.toList());

        return ApiResponse.<Void>builder()
                .success(false)
                .message("Validation failed")
                .errors(errors)
                .timestamp(System.currentTimeMillis())
                .path(request.getRequestURI())
                .build();
    }
}
Enter fullscreen mode Exit fullscreen mode

Key Takeaways

✅ ApiResponse INBUILT NAHI HAI
✅ Khud banana padta hai (Industry standard)
✅ Consistency ke liye important
✅ Error handling easy hoti hai
✅ Frontend integration smooth hota hai

❌ Spring Boot mein koi default wrapper nahi
❌ ResponseEntity sirf HTTP control deta hai
Enter fullscreen mode Exit fullscreen mode

Quick Template - Copy Paste Karo

package com.yourcompany.dto.response;

import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.*;
import java.util.List;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ApiResponse<T> {
    private Boolean success;
    private String message;
    private T data;
    private List<String> errors;
    private Long timestamp;

    public static <T> ApiResponse<T> success(T data) {
        return ApiResponse.<T>builder()
                .success(true)
                .data(data)
                .timestamp(System.currentTimeMillis())
                .build();
    }

    public static <T> ApiResponse<T> success(String message, T data) {
        return ApiResponse.<T>builder()
                .success(true)
                .message(message)
                .data(data)
                .timestamp(System.currentTimeMillis())
                .build();
    }

    public static <T> ApiResponse<T> error(String message) {
        return ApiResponse.<T>builder()
                .success(false)
                .message(message)
                .timestamp(System.currentTimeMillis())
                .build();
    }

    public static <T> ApiResponse<T> error(String message, List<String> errors) {
        return ApiResponse.<T>builder()
                .success(false)
                .message(message)
                .errors(errors)
                .timestamp(System.currentTimeMillis())
                .build();
    }
}
Enter fullscreen mode Exit fullscreen mode

Yeh file apne project mein add karo aur use karo! 🚀

Top comments (0)