Backend Development 11 min read

Mastering Spring Boot RestClient: When to Choose RestTemplate, WebClient, or RestClient

This article introduces Spring Boot's new RestClient, compares it with RestTemplate and WebClient, and provides detailed code examples for creating clients, performing GET/POST/PUT/DELETE requests, handling errors, customizing interceptors, and enabling load‑balanced service discovery.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Mastering Spring Boot RestClient: When to Choose RestTemplate, WebClient, or RestClient

1. Introduction

Since Spring Framework 6.1 and Spring Boot 3.2, Spring RestClient provides a fluent and synchronous API for HTTP requests, built on underlying HTTP client libraries such as JDK HttpClient or Apache HttpComponents.

RestClient combines the fluent design of WebClient with the functionality of RestTemplate and is designed for testability, making HTTP interaction mocking easier in unit tests. For asynchronous or streaming scenarios, WebClient remains the preferred API.

RestTemplate, RestClient and WebClient – How to Choose?

From Spring 6.1, RestClient offers a modern API for synchronous HTTP access, whereas RestTemplate is an older, bulky class with many overloads. WebClient also supports synchronous calls but requires the spring-boot-starter-webflux dependency.

RestTemplate is an older synchronous API lacking modern flexibility.

WebClient is the reactive, non‑blocking client from Spring WebFlux; it can be used synchronously but is often overkill for simple use cases.

RestClient is a new addition that aims to replace RestTemplate, offering a modern, fluent API without needing a reactive stack, thus balancing the two.

Comparison

Feature – Reactive programming: WebClient supports reactive programming; RestTemplate is synchronous.

Technology stack: WebClient is built on the reactive stack; RestTemplate uses the Servlet stack.

Thread model: WebClient uses non‑blocking I/O for high concurrency; RestTemplate uses blocking I/O.

Java version: WebClient requires Java 8+ and supports functional programming; RestTemplate works with Java 6+.

Error handling: WebClient provides operators like onErrorResume; RestTemplate typically uses try‑catch.

Streaming: WebClient supports Flux/Mono for streaming; RestTemplate has limited streaming support.

Use case: WebClient suits microservices and reactive applications; RestTemplate fits traditional monolithic apps.

Dependencies: WebClient needs Spring WebFlux; RestTemplate needs Spring Web.

Feature support: WebClient aligns with reactive programming and receives ongoing support; RestTemplate may receive limited updates.

2. Practical Examples

2.1 Creating a RestClient

Spring can create a RestClient bean using RestClient.create(baseURI) or a builder for more complex configuration, such as setting default headers or a custom HttpClient.

<code>@Value("${pack.remote.address:http://www.pack.com}")
private String baseURI;

@Bean
public RestClient restClient() {
    return RestClient.create(baseURI);
}
</code>

Using a builder with a custom request factory:

<code>@Bean
public HttpComponentsClientHttpRequestFactory clientHttpRequestFactory() {
    HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
    factory.setHttpClient(httpClient);
    // more configuration
    return factory;
}

@Bean
public RestClient restClient(CloseableHttpClient httpClient) {
    return RestClient.builder()
        .baseUrl(baseURI)
        .requestFactory(clientHttpRequestFactory())
        .build();
}
</code>

RestClient can also be created from an existing RestTemplate:

<code>@Bean
public RestClient restClient(RestTemplate restTemplate) {
    return RestClient.create(restTemplate);
}
</code>

2.2 HTTP GET

Use restClient.get() to issue GET requests, optionally passing URI template variables.

<code>@Resource
private RestClient restClient;

restClient.get()
    .uri("/users")
    // ...

restClient.get()
    .uri("/employees/{id}", id)
    // ...
</code>

Retrieve the response body as a list of users:

<code>List<User> list = restClient.get()
    .uri("/users")
    .accept(MediaType.APPLICATION_JSON)
    .retrieve()
    .body(List.class);
</code>

Or obtain a ResponseEntity for full response details:

<code>ResponseEntity<List> responseEntity = restClient.get()
    .uri("/users")
    .accept(MediaType.APPLICATION_JSON)
    .retrieve()
    .toEntity(List.class);
</code>

2.3 HTTP POST

Use restClient.post() for POST requests. The toBodilessEntity() method behaves like retrieve() but discards the response body.

<code>User user = new User(666L, "Zhang San", 22);
ResponseEntity<Void> responseEntity = restClient.post()
    .uri("/users")
    .contentType(MediaType.APPLICATION_JSON)
    .body(user)
    .retrieve()
    .toBodilessEntity();
</code>

2.4 HTTP PUT

<code>User user = new User(666L, "Zhang San", 22);
ResponseEntity<User> responseEntity = restClient.put()
    .uri("/users/666")
    .contentType(MediaType.APPLICATION_JSON)
    .accept(MediaType.APPLICATION_JSON)
    .body(user)
    .retrieve()
    .toEntity(User.class);
</code>

2.5 HTTP DELETE

<code>ResponseEntity<Employee> responseEntity = restClient.delete()
    .uri("/users/666")
    .retrieve()
    .toBodilessEntity();
</code>

2.6 Advanced Usage with exchange()

For full control over request and response, use exchange() and handle status codes manually.

<code>List<Employee> list = restClient.get()
    .uri("/users")
    .accept(MediaType.APPLICATION_JSON)
    .exchange((request, response) -> {
        List responseBody = null;
        if (response.getStatusCode().is4xxClientError() ||
            response.getStatusCode().is5xxServerError()) {
            System.err.println("Request error");
        } else {
            responseBody = new ObjectMapper().readValue(response.getBody(), List.class);
        }
        return responseBody;
    });
</code>

2.7 Exception Handling

RestClient throws HttpClientErrorException for 4xx responses and HttpServerErrorException for 5xx responses.

<code>try {
    User user = restClient.get()
        .uri("/users/666")
        .accept(MediaType.APPLICATION_JSON)
        .retrieve()
        .body(User.class);
} catch (HttpClientErrorException e4xx) {
    // handle client error
} catch (HttpServerErrorException e5xx) {
    // handle server error
}
</code>

2.8 Custom Interceptor

Configure a custom interceptor via RestClient.builder() to add logging, authentication, etc.

<code>@Bean
public RestClient demoRestClient(LoggingRestClientInterceptor loggingInterceptor) {
    return RestClient.builder()
        .requestInterceptor(loggingInterceptor)
        .build();
}
</code>
<code>@Component
public static class LoggingRestClientInterceptor implements ClientHttpRequestInterceptor {
    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body,
                                        ClientHttpRequestExecution execution) throws IOException {
        System.err.println("logging...");
        return execution.execute(request, body);
    }
}
</code>

2.9 Load‑Balanced RestClient with Service Discovery

From Spring Cloud 4.1.0, RestClient supports load‑balanced calls via service discovery. Define a RestClient.Builder bean with @LoadBalanced and use it to create a RestClient.

<code>@Bean
@LoadBalanced
public RestClient.Builder demoRestClientBuilder() {
    Builder builder = RestClient.builder().baseUrl("http://demo");
    return builder;
}

@Bean
public RestClient lbcRestClient(RestClient.Builder builder) {
    return builder.baseUrl("http://demo").build();
}
</code>
backendJavaSpring BootRestClientHTTP Client
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.