Create REST Client using WebClient for Spring Boot CRUD REST API

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 

In your src/main/resources/application.properties, add the following MySQL database 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: []

Comments