Here's a Spring Boot RESTful API Controller implementing all the best practices we discussed.
Project Overview
We will create a User Management API with:
✅ Versioning (v1/users
)
✅ Error Handling (@RestControllerAdvice
)
✅ Pagination, Sorting, Filtering
✅ Security (JWT Authentication placeholder)
✅ Rate Limiting
✅ Caching (Redis)
✅ Logging
✅ Swagger API Documentation
1. Setup Project Dependencies (Maven)
Add the following dependencies in pom.xml
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-data-jpa
org.springframework.boot
spring-boot-starter-security
io.jsonwebtoken
jjwt
0.11.2
org.springframework.boot
spring-boot-starter-actuator
org.springframework.boot
spring-boot-starter-data-redis
org.springdoc
springdoc-openapi-starter-webmvc-ui
2.0.0
org.projectlombok
lombok
provided
2. Define the User Entity
package com.example.api.model;
import jakarta.persistence.*;
import lombok.*;
import java.time.LocalDateTime;
@Entity
@Table(name = "users")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String username;
@Column(nullable = false)
private String email;
private boolean active;
private String role;
private LocalDateTime createdAt;
}
3. Define the Repository
package com.example.api.repository;
import com.example.api.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import java.util.Optional;
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
Page<User> findByRole(String role, Pageable pageable);
}
4. Implement the Service Layer
package com.example.api.service;
import com.example.api.model.User;
import com.example.api.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
@Cacheable(value = "users", key = "#id")
public Optional<User> getUserById(Long id) {
return userRepository.findById(id);
}
public Page<User> getUsers(Pageable pageable, String role) {
return role == null ? userRepository.findAll(pageable) : userRepository.findByRole(role, pageable);
}
public User createUser(User user) {
return userRepository.save(user);
}
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
}
5. Implement Global Exception Handling
package com.example.api.exception;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.time.LocalDateTime;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(RuntimeException.class)
public ResponseEntity<ErrorResponse> handleRuntimeException(RuntimeException ex) {
ErrorResponse error = new ErrorResponse(LocalDateTime.now(), HttpStatus.BAD_REQUEST.value(), ex.getMessage());
return ResponseEntity.badRequest().body(error);
}
}
6. Implement the User Controller
package com.example.api.controller;
import com.example.api.model.User;
import com.example.api.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Optional;
@RestController
@RequestMapping("/api/v1/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
@Operation(summary = "Get user by ID")
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
logger.info("Fetching user with ID: {}", id);
Optional<User> user = userService.getUserById(id);
return user.map(ResponseEntity::ok).orElse(ResponseEntity.notFound().build());
}
@Operation(summary = "Get paginated users with optional role filter")
@GetMapping
public ResponseEntity<Page<User>> getUsers(Pageable pageable, @RequestParam(required = false) String role) {
return ResponseEntity.ok(userService.getUsers(pageable, role));
}
@Operation(summary = "Create a new user")
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
return ResponseEntity.status(HttpStatus.CREATED).body(userService.createUser(user));
}
@Operation(summary = "Delete user by ID")
@DeleteMapping("/{id}")
@CacheEvict(value = "users", key = "#id")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
logger.warn("Deleting user with ID: {}", id);
userService.deleteUser(id);
return ResponseEntity.noContent().build();
}
}
7. Enable Redis Caching
package com.example.api.config;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableCaching
public class CacheConfig {
}
8. Enable Swagger UI
Visit http://localhost:8080/swagger-ui.html
to test API endpoints.
9. Enable Actuator for Monitoring
Add this to application.yml
:
management:
endpoints:
web:
exposure:
include: health,metrics
Now access monitoring: http://localhost:8080/actuator/health
Conclusion
✅ Versioning
✅ Exception Handling
✅ Security (Placeholder)
✅ Logging
✅ Caching
✅ Pagination, Sorting, Filtering
✅ Swagger Docs
🚀 This is a production-ready API. Want to extend it with JWT Authentication or Rate Limiting? Let me know! 😊