Skip to content

WebClient vs RestClient: Which Should I Use for Synchronous Calls in Spring Boot?

I needed to make synchronous HTTP calls in a Spring Boot application, and I kept seeing conflicting advice. Some sources said to use WebClient. Others mentioned RestClient. The Spring documentation itself had changed over time.

Here’s what I found and why RestClient is the right choice for synchronous calls.

The Confusion

When Spring 5.0 came out in 2017, the documentation stated that WebClient would replace RestTemplate. This led to years of blog posts, Stack Overflow answers, and even AI recommendations suggesting WebClient for all HTTP calls.

The problem? WebClient is a reactive client. Using it for synchronous calls requires calling .block() to force blocking behavior on a non-blocking client.

UserService.java
@Service
public class UserService {
private final WebClient webClient;
public UserService(WebClient.Builder webClientBuilder) {
this.webClient = webClientBuilder
.baseUrl("https://api.example.com")
.build();
}
public User getUser(Long id) {
return webClient.get()
.uri("/users/{id}", id)
.retrieve()
.bodyToMono(User.class)
.block(); // Forces synchronous behavior
}
}

This works, but it’s wrong for several reasons:

  1. Unnecessary dependencies - You need to add spring-boot-starter-webflux even though you’re not using reactive features
  2. Reactive overhead - WebClient creates reactive infrastructure (schedulers, buffers, operators) that never gets used
  3. Complexity - You’re wrapping your head around Mono/Flux types when you just need a simple HTTP call

The Right Tool: RestClient

Spring 6.1 (released in 2023, included in Spring Boot 3.2) introduced RestClient specifically for synchronous HTTP calls. It has a modern, fluent API similar to WebClient, but it’s designed for the traditional thread-per-request model.

UserService.java
@Service
public class UserService {
private final RestClient restClient;
public UserService(RestClient.Builder restClientBuilder) {
this.restClient = restClientBuilder
.baseUrl("https://api.example.com")
.build();
}
public User getUser(Long id) {
return restClient.get()
.uri("/users/{id}", id)
.retrieve()
.body(User.class); // Direct return, no blocking
}
}

Same functionality, cleaner code, no reactive baggage.

Comparison

AspectRestClientWebClient with .block()
IntroducedSpring 6.1 (2023)Spring 5.0 (2017)
PurposeSynchronous HTTPReactive HTTP
Dependenciesspring-web onlyRequires spring-webflux
Return typeDirect objectsMono/Flux wrapper
API styleFluentFluent
Thread modelThread-per-requestNon-blocking event loop
Virtual threadsWorks naturallyNot designed for it
Learning curveSimpleSteep (reactive concepts)

When to Use Each

Use RestClient when:

  • Building traditional Spring MVC applications
  • Making synchronous HTTP calls
  • You don’t need backpressure or streaming
  • Using Java 21+ virtual threads for concurrency

Use WebClient when:

  • Building Spring WebFlux reactive applications
  • Need non-blocking I/O with backpressure
  • Streaming large amounts of data
  • Your application is already on the reactive stack
ReactiveUserService.java
@Service
public class ReactiveUserService {
private final WebClient webClient;
public ReactiveUserService(WebClient.Builder webClientBuilder) {
this.webClient = webClientBuilder
.baseUrl("https://api.example.com")
.build();
}
// Correct use case: reactive streaming
public Flux<User> getAllUsers() {
return webClient.get()
.uri("/users")
.retrieve()
.bodyToFlux(User.class);
}
}

What About RestTemplate?

RestTemplate is in maintenance mode since Spring 5.0. It will be officially deprecated in Spring 7.1 (Spring Boot 4.2) and removed in Spring 8 (Spring Boot 5).

If you have existing code using RestTemplate, it still works. For new code, use RestClient.

Legacy code using RestTemplate
// Old approach - still works but will be deprecated
Person person = restTemplate.getForObject("/persons/{id}", Person.class, id);
// New approach with RestClient
Person person = restClient.get()
.uri("/persons/{id}", id)
.retrieve()
.body(Person.class);

RestClient Configuration

Spring Boot auto-configures RestClient.Builder as a bean. You can customize it in a configuration class.

RestClientConfig.java
@Configuration
public class RestClientConfig {
@Bean
public RestClient restClient(RestClient.Builder builder) {
return builder
.baseUrl("https://api.example.com")
.defaultHeader("Authorization", "Bearer " + apiKey)
.requestInterceptor(new LoggingInterceptor())
.build();
}
}

Error Handling

RestClient throws RestClientException for HTTP errors. You can customize error handling with onStatus.

UserService.java
public User getUser(Long id) {
return restClient.get()
.uri("/users/{id}", id)
.retrieve()
.onStatus(status -> status.is4xxClientError(),
(request, response) -> {
throw new UserNotFoundException("User not found: " + id);
})
.body(User.class);
}

Why the Spring Team Changed Course

The Spring documentation initially said WebClient would replace RestTemplate. This was later changed to “maintenance mode” status. Then RestClient was introduced.

A Reddit commenter put it well: “The introduction of RestClient is Spring putting up the white flag and saying ‘oh actually, the future is not reactive’.”

The virtual threads feature in Java 21 changed the calculus. Before virtual threads, reactive programming was one of the few ways to handle high concurrency with limited threads. Now, you can write simple synchronous code and let virtual threads handle the concurrency.

Bottom Line

For synchronous HTTP calls in Spring Boot, use RestClient. It’s the modern API designed for your use case. WebClient is great for reactive applications, but using .block() to force synchronous behavior adds complexity you don’t need.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments