Developing and consuming RESTful APIs are common tasks for modern web applications. Spring Boot simplifies the development of microservices with its comprehensive suite of tools for building web servers. But when it comes to consuming these APIs, Spring Boot also has a powerful reactive rest client called WebClient. WebClient is a non-blocking, reactive client for performing HTTP requests with Spring. In this post, we will walk through how to create a REST client using WebClient to consume a CRUD REST API built with Spring Boot.
First, we will build Spring Boot CRUD REST API application using Spring Boot 3, Spring Data JPA (Hibernate), and MySQL database, and then we will build the REST Client using the WebClient class.
Pre-requisites
- You have Java and MySQL set up.
- You have a basic understanding of REST APIs, JPA/Hibernate, and Maven/Gradle.
- You have an IDE like IntelliJ IDEA, Eclipse, or Spring Tool Suite (STS).
Here’s a step-by-step guide to creating a one-to-one REST API.
1. Set Up the Spring Boot Project
Create a Spring Boot project using Spring Initializr with the following dependencies:
- Spring Web
- Spring Data JPA
- MySQL Driver
- Spring Reactive Web
2. Configure MySQL in application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/demo
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
Make sure to replace the database, username, and password as per your MySQL database details.3. Define JPA Entity
Create User JPA entity:
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
// Getters and Setters
}
4. Create the Repository Layer
Create UserRepository interface that extends JpaRepository:
public interface UserRepository extends JpaRepository<User, Long> {
// CRUD methods are inherited
}
5. Implement CRUD operations in the Service Layer
First, we'll add the full CRUD functionality to our UserService interface and then implement these methods in the UserServiceImpl class.
UserService.java:
public interface UserService {
User createUser(User user);
User getUserById(Long id);
User updateUser(Long id, User userDetails);
void deleteUser(Long id);
List<User> getAllUsers();
}
UserServiceImpl.java:
@Service
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
@Autowired
public UserServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public User createUser(User user) {
return userRepository.save(user);
}
@Override
public User getUserById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new EntityNotFoundException("User not found with id " + id));
}
@Override
public User updateUser(Long id, User userDetails) {
User user = userRepository.findById(id)
.orElseThrow(() -> new EntityNotFoundException("User not found with id " + id));
user.setUsername(userDetails.getUsername());
// Update other fields...
return userRepository.save(user);
}
@Override
public void deleteUser(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new EntityNotFoundException("User not found with id " + id));
userRepository.delete(user);
}
@Override
public List<User> getAllUsers() {
return userRepository.findAll();
}
}
6. Implement CRUD operations in the Controller Layer
Now let's create the REST endpoints for these CRUD operations in the UserController.
UserController.java:
@RestController
@RequestMapping("/users")
public class UserController {
private final UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
User newUser = userService.createUser(user);
return new ResponseEntity<>(newUser, HttpStatus.CREATED);
}
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
User user = userService.getUserById(id);
return ResponseEntity.ok(user);
}
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User userDetails) {
User updatedUser = userService.updateUser(id, userDetails);
return ResponseEntity.ok(updatedUser);
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
return ResponseEntity.noContent().build();
}
@GetMapping
public ResponseEntity<List<User>> getAllUsers() {
List<User> users = userService.getAllUsers();
return ResponseEntity.ok(users);
}
}
7. Create a REST Client with WebClient
To use WebClient, you must include the spring-boot-starter-webflux dependency in your project.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
RestClient.java:
@Component
public class RestClient {
private final WebClient webClient;
public RestClient(@Value("${api.base.url}") String baseUrl) {
this.webClient = WebClient.builder()
.baseUrl("http://localhost:8080")
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.build();
}
public User createUser(User user) {
return webClient.post()
.uri("/users")
.bodyValue(user)
.retrieve()
.bodyToMono(User.class)
.block();
}
public User getUserById(Long id) {
return webClient.get()
.uri("/users/" + id)
.retrieve()
.bodyToMono(User.class)
.block();
}
public User updateUser(Long id, User user) {
return webClient.put()
.uri("/users/" + id)
.bodyValue(user)
.retrieve()
.bodyToMono(User.class)
.block();
}
public void deleteUser(Long id) {
webClient.delete()
.uri("/users/" + id)
.retrieve()
.bodyToMono(Void.class)
.block();
}
public List<User> getAllUsers() {
return webClient.get()
.uri("/users")
.retrieve()
.bodyToFlux(User.class)
.collectList()
.block();
}
}
Using the REST Client
This RestClient could be used within a Spring Boot application to call your own REST API or for testing purposes.
To demonstrate its usage, you can create a CommandLineRunner bean that uses this RestClient to interact with your API.
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public CommandLineRunner run(RestClient client) {
return args -> {
User newUser = new User();
newUser.setUsername("newuser");
// Set other properties...
// Create User
User createdUser = client.createUser(newUser);
System.out.println("Created User: " + createdUser);
// Get User
User fetchedUser = client.getUserById(createdUser.getId());
System.out.println("Fetched User: " + fetchedUser);
// Update User
fetchedUser.setUsername("updateduser");
User updatedUser = client.updateUser(fetchedUser.getId(), fetchedUser);
System.out.println("Updated User: " + updatedUser);
// Delete User
client.deleteUser(updatedUser.getId());
System.out.println("Deleted User");
// Get All Users
List<User> users = client.getAllUsers();
System.out.println("All Users: " + users);
};
}
}
Output:
Created User: User{id=1, username='Ramesh'} Fetched User: User{id=1, username='Ramesh'} Updated User: User{id=1, username='Ramesh_updated'} Deleted User All Users: []