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.

π 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:
1 2 3 4 5 6 7 |
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> |
π§ Configure WebClient Bean (Optional)
You can define a WebClient
bean for reuse across services.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@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)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
@Service public class UserService { private final WebClient webClient; public UserService(WebClient webClient) { this.webClient = webClient; } public List<User> getUsers() { return webClient.get() .uri("/users") .retrieve() .bodyToFlux(User.class) .collectList() .block(); // blocking for simplicity } } |
π¦ User DTO
1 2 3 4 5 6 7 8 9 |
@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
1 2 3 4 5 6 7 8 9 10 11 |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public Mono<User> 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.
1 2 3 4 5 6 7 8 9 |
public Mono<User> 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:
1 2 3 4 5 6 7 8 9 |
@Test void testGetUser() { WebClient webClient = WebClient.create("http://mock-api"); // inject this mock WebClient in service and test responses } |
π Complete Controller Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
@RestController @RequestMapping("/api/users") public class UserController { private final UserService userService; public UserController(UserService userService) { this.userService = userService; } @GetMapping public ResponseEntity<List<User>> getAllUsers() { return ResponseEntity.ok(userService.getUsers()); } @PostMapping public ResponseEntity<User> 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.