Calling Rest API using WebClient in Spring Boot

  • Last updated Apr 25, 2024

In this tutorial, we will guide you through the process of using Spring WebClient to seamlessly make REST API calls within a Spring Boot application.

The WebClient is an HTTP client included in Spring WebFlux, a part of the Spring Framework that supports the development of reactive, non-blocking web applications. Spring WebFlux provides an alternative to the traditional Spring MVC framework, which is based on the Servlet API and is primarily designed for blocking I/O. The use of WebClient simplifies the interaction with external services, offering a reactive and non-blocking approach for efficient communication. By following these instructions, you'll learn how to set up WebClient, create a dedicated service for API calls, and integrate it into your Spring Boot application. This enables you to handle RESTful interactions with ease and flexibility.

Add Dependencies

Add the necessary dependencies to your pom.xml or build.gradle file:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
implementation 'org.springframework.boot:spring-boot-starter-webflux'
Create Service

Create a service class with basic HTTP methods such as GET, POST, PUT, and DELETE. Within these methods, use a WebClient object to call the REST API:

import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

@Service
public class ApiService {

  
  private WebClient.Builder webClient = WebClient.builder();

  public <T> Mono<T> get(String baseUrl, String path, Class<T> responseType) {
    return webClient.baseUrl(baseUrl).build().get().uri(path).retrieve().bodyToMono(responseType);
  }

  public <T> Mono<T> post(String baseUrl, String path, Object request, Class<T> responseType) {
    return webClient.baseUrl(baseUrl).build().post().uri(path)
        .body(BodyInserters.fromValue(request)).retrieve().bodyToMono(responseType);
  }

  public <T> Mono<T> put(String baseUrl, String path, Object request, Class<T> responseType) {
    return webClient.baseUrl(baseUrl).build().put().uri(path).body(BodyInserters.fromValue(request))
        .retrieve().bodyToMono(responseType);
  }

  public <T> Mono<T> delete(String baseUrl, String path, Class<T> responseType) {
    return webClient.baseUrl(baseUrl).build().delete().uri(path).retrieve()
        .bodyToMono(responseType);
  }

}
Calling the APIs

Here's an example making HTTP calls:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tutorialsbuddy.webclientexample.client.ApiService;
import com.tutorialsbuddy.webclientexample.dto.ApiResponse;
import jakarta.servlet.http.HttpServletRequest;
import reactor.core.publisher.Mono;

@Service
public class StudentServiceImpl {

  @Autowired
  private ApiService apiService;

  public ApiResponse getStudentProfile(Long studentId, HttpServletRequest httpRequest) {
    String baseUrl = "http://localhost:8060";
    String getPath = "/users/" + studentId + "/payments";

    // Perform GET request
    Mono<String> getResponseMono = apiService.get(baseUrl, getPath, String.class);

    // Wait for the result and handle the response
    String response = getResponseMono.block();

    // Construct ApiResponse based on your logic
    if (response != null) {
      // Successful response
      ObjectMapper mapper = new ObjectMapper();
      Object data = null;
      try {
        data = mapper.readValue(response, Object.class);
      } catch (JsonProcessingException e) {
        System.out.println("Error: " + e);
      }
      return ApiResponse.builder().data(data).success(true).errorCode(0).message("Success").build();
    } else {
      // Handle error or empty response
      return ApiResponse.builder().data(response).success(false).errorCode(-1)
          .message("Failed to retrieve student profile").build();
    }
  }

}