Loading...
Loading...
Implement REST API design standards and best practices for Spring Boot projects. Use when creating or reviewing REST endpoints, DTOs, error handling, pagination, security headers, HATEOAS and architecture patterns.
npx skill4agent add giuseppe-trisciuoglio/developer-kit spring-boot-rest-api-standards@Data@Value@Valid@RequestBody@NotBlank@Email@SizeMethodArgumentNotValidException@RestControllerAdviceResponseStatusException@RestController
@RequestMapping("/v1/users")
@RequiredArgsConstructor
@Slf4j
public class UserController {
private final UserService userService;
@GetMapping
public ResponseEntity<Page<UserResponse>> getAllUsers(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int pageSize) {
log.debug("Fetching users page {} size {}", page, pageSize);
Page<UserResponse> users = userService.getAll(page, pageSize);
return ResponseEntity.ok(users);
}
@GetMapping("/{id}")
public ResponseEntity<UserResponse> getUserById(@PathVariable Long id) {
return ResponseEntity.ok(userService.getById(id));
}
@PostMapping
public ResponseEntity<UserResponse> createUser(@Valid @RequestBody CreateUserRequest request) {
UserResponse created = userService.create(request);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}
@PutMapping("/{id}")
public ResponseEntity<UserResponse> updateUser(
@PathVariable Long id,
@Valid @RequestBody UpdateUserRequest request) {
return ResponseEntity.ok(userService.update(id, request));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.delete(id);
return ResponseEntity.noContent().build();
}
}// Request DTO
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CreateUserRequest {
@NotBlank(message = "User name cannot be blank")
private String name;
@Email(message = "Valid email required")
private String email;
}
// Response DTO
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserResponse {
private Long id;
private String name;
private String email;
private LocalDateTime createdAt;
}@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidationException(
MethodArgumentNotValidException ex, WebRequest request) {
String errors = ex.getBindingResult().getFieldErrors().stream()
.map(f -> f.getField() + ": " + f.getDefaultMessage())
.collect(Collectors.joining(", "));
ErrorResponse errorResponse = new ErrorResponse(
HttpStatus.BAD_REQUEST.value(),
"Validation Error",
"Validation failed: " + errors,
request.getDescription(false).replaceFirst("uri=", "")
);
return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(ResponseStatusException.class)
public ResponseEntity<ErrorResponse> handleResponseStatusException(
ResponseStatusException ex, WebRequest request) {
ErrorResponse error = new ErrorResponse(
ex.getStatusCode().value(),
ex.getStatusCode().toString(),
ex.getReason(),
request.getDescription(false).replaceFirst("uri=", "")
);
return new ResponseEntity<>(error, ex.getStatusCode());
}
}@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
private final EmailService emailService;
// Dependencies are explicit and testable
}// Java records (JDK 16+)
public record UserResponse(Long id, String name, String email, LocalDateTime createdAt) {}
// Lombok @Value for immutability
@Value
public class UserResponse {
Long id;
String name;
String email;
LocalDateTime createdAt;
}@PostMapping
public ResponseEntity<UserResponse> createUser(@Valid @RequestBody CreateUserRequest request) {
// Validation happens automatically before method execution
return ResponseEntity.status(HttpStatus.CREATED).body(userService.create(request));
}return ResponseEntity.status(HttpStatus.CREATED)
.header("Location", "/api/users/" + created.getId())
.header("X-Total-Count", String.valueOf(userService.count()))
.body(created);@Service
@Transactional
public class UserService {
@Transactional(readOnly = true)
public Optional<User> findById(Long id) {
return userRepository.findById(id);
}
@Transactional
public User create(User user) {
return userRepository.save(user);
}
}@Slf4j
@Service
public class UserService {
public User create(User user) {
log.info("Creating user with email: {}", user.getEmail());
return userRepository.save(user);
}
}/**
* Retrieves a user by id.
*
* @param id the user id
* @return ResponseEntity containing a UserResponse
* @throws ResponseStatusException with 404 if user not found
*/
@GetMapping("/{id}")
public ResponseEntity<UserResponse> getUserById(@PathVariable Long id)@Autowiredreferences/agents/spring-boot-code-review-specialist.mdspring-boot-dependency-injection/SKILL.mdjunit-test-patterns/SKILL.md