Consuming External APIs in Spring Boot using WebClient

When building microservices or applications that rely on external systems, you often need to call third-party REST APIs. While RestTemplate has been widely used for this, WebClient is the modern and more powerful alternative introduced in Spring 5 as part of the reactive web stack.

In this post, you’ll learn how to consume external APIs in Spring Boot using WebClient β€” with complete examples and best practices.

Consuming External APIs in Spring Boot using WebClient

πŸ”„ What is WebClient?

WebClient is a non-blocking, reactive HTTP client. It supports both synchronous and asynchronous calls and is part of the spring-webflux module.

βš™οΈ Add WebClient Dependency

If you are using Spring Boot 2.4+, WebClient comes bundled with spring-boot-starter-webflux.

Maven:



    org.springframework.boot
    spring-boot-starter-webflux


πŸ”§ Configure WebClient Bean (Optional)

You can define a WebClient bean for reuse across services.


@Configuration
public class WebClientConfig {

    @Bean
    public WebClient webClient(WebClient.Builder builder) {
        return builder
                .baseUrl("https://jsonplaceholder.typicode.com") // default base URL
                .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .build();
    }
}

πŸ“‘ Making a GET Request

Let’s create a simple service to fetch a list of users from a public API.

βœ… Example: Fetch Users (GET)


@Service
public class UserService {

    private final WebClient webClient;

    public UserService(WebClient webClient) {
        this.webClient = webClient;
    }

    public List getUsers() {
        return webClient.get()
                .uri("/users")
                .retrieve()
                .bodyToFlux(User.class)
                .collectList()
                .block(); // blocking for simplicity
    }
}

πŸ“¦ User DTO


@Data
public class User {
    private Long id;
    private String name;
    private String email;
}

🧾 Making a POST Request

To send data using POST:

βœ… Example: Create a User


public User createUser(User user) {
    return webClient.post()
            .uri("/users")
            .bodyValue(user)
            .retrieve()
            .bodyToMono(User.class)
            .block();
}

🚨 Handling Errors Gracefully

Error handling is crucial when calling External APIs in Spring Boot.


public Mono getUserById(Long id) {
    return webClient.get()
            .uri("/users/{id}", id)
            .retrieve()
            .onStatus(HttpStatus::is4xxClientError, response -> {
                return Mono.error(new RuntimeException("Client error: " + response.statusCode()));
            })
            .onStatus(HttpStatus::is5xxServerError, response -> {
                return Mono.error(new RuntimeException("Server error: " + response.statusCode()));
            })
            .bodyToMono(User.class);
}

πŸ”„ Non-Blocking Asynchronous Calls

WebClient supports reactive programming with Mono/Flux for async operations.


public Mono getUserAsync(Long id) {
    return webClient.get()
            .uri("/users/{id}", id)
            .retrieve()
            .bodyToMono(User.class);
}

Use .subscribe() or reactive chains to work with this non-blocking result.

πŸ§ͺ Unit Testing WebClient

Use WebClient.Builder with a MockWebServer or mock the WebClient directly.

Example with Mockito:


@Test
void testGetUser() {
    WebClient webClient = WebClient.create("http://mock-api");

    // inject this mock WebClient in service and test responses
}

πŸ“‹ Complete Controller Example


@RestController
@RequestMapping("/api/users")
public class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping
    public ResponseEntity> getAllUsers() {
        return ResponseEntity.ok(userService.getUsers());
    }

    @PostMapping
    public ResponseEntity createUser(@RequestBody User user) {
        return ResponseEntity.ok(userService.createUser(user));
    }
}

🧠 Best Practices

  • Prefer non-blocking (Mono/Flux) in reactive applications.
  • Use WebClient.Builder for customization and reusability.
  • Implement retry logic with .retry() for transient failures.
  • Use timeout with .timeout(Duration.ofSeconds(5)) for stability.
  • Avoid .block() in production unless absolutely necessary.

βœ… Conclusion

WebClient is the go-to HTTP client for modern Spring Boot applications. It is lightweight, non-blocking, and powerful β€” making it suitable for both synchronous and reactive use cases. With proper error handling and design, you can safely and efficiently integrate with external APIs.

Reference

Consuming WebClients