Best Practices for Building RESTful APIs with Spring Boot
Lessons from real projects on designing Spring Boot RESTful APIs: unified response envelopes, global exception handling, validation, and API versioning.
Introduction#
Over the past year I have worked on several Spring Boot backend projects and gradually built up a set of opinions about RESTful API design. This post collects the practices I consider most important.
1. A unified response envelope#
A consistent response shape lowers communication cost during both development and debugging. I usually go with:
{
"code": 200,
"message": "success",
"data": {}
}jsonThe Java side:
@Data
public class ApiResponse<T> {
private Integer code;
private String message;
private T data;
public static <T> ApiResponse<T> success(T data) {
ApiResponse<T> response = new ApiResponse<>();
response.setCode(200);
response.setMessage("success");
response.setData(data);
return response;
}
public static <T> ApiResponse<T> error(Integer code, String message) {
ApiResponse<T> response = new ApiResponse<>();
response.setCode(code);
response.setMessage(message);
return response;
}
}java2. Global exception handling#
With @RestControllerAdvice and @ExceptionHandler, exception handling can be centralised cleanly:
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ApiResponse<?> handleValidation(MethodArgumentNotValidException ex) {
String msg = ex.getBindingResult().getFieldErrors()
.stream()
.map(e -> e.getField() + ": " + e.getDefaultMessage())
.collect(Collectors.joining(", "));
return ApiResponse.error(400, msg);
}
@ExceptionHandler(Exception.class)
public ApiResponse<?> handleException(Exception ex) {
return ApiResponse.error(500, "Internal server error");
}
}java3. Request validation#
Spring Boot ships with Bean Validation; combined with @Valid it makes request validation straightforward:
@RestController
@RequestMapping("/api/users")
public class UserController {
@PostMapping
public ApiResponse<User> create(@Valid @RequestBody CreateUserRequest request) {
// business logic
}
}java4. API versioning#
Keeping the version in the URL path is the option I recommend:
/api/v1/users/api/v2/users
That makes a breaking upgrade a gradual transition rather than a flag day.
Summary#
These are the practices I rely on when building RESTful APIs with Spring Boot. Good API design should be settled at the start of a project to avoid rework later. Hope it helps.