Spring Boot 3.2 and Spring Framework 6.1 introduce a new feature called RestClient, which is a fresh synchronous way to communicate over HTTP. It’s similar to WebClient in its smooth way of handling requests but is built on the foundations of RestTemplate. In this article we will learn how to get started with Spring Boot RestClient in a minute.
Spring Rest Client vs RestTemplate
Firstly, let’s highlight some advantages of the new REST Client API compared with the REST Template:
- Functional API: RestClient provides a functional API that is more concise and easier to read and write.
- Reactive Streams support: RestClient supports reactive streams, making it easy to consume asynchronous REST APIs.
- Type inference: RestClient can infer the type of the response from the method signature, eliminating the need for manual type casting.
An example of RestClient
Let’s build a quick example of a SpringBoot application Class which queries a public available REST API https://jsonplaceholder.typicode.com/users .
Firstly, create a new Spring Boot 3.2 project which includes the Web Starter in it:
Adding the Model
Since our application will fetch a list of User objects, we will add a Java Record to store all users from the REST Service
public record User(Integer id, String name, String username, String email ){ }
Coding the REST Client
Next, to make things as simple as possible, we will add the RESTClient directly in the @SpringBootApplication Class:
package com.example.demo; import java.util.List; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.core.ParameterizedTypeReference; import org.springframework.web.client.RestClient; @SpringBootApplication public class MainApplication implements CommandLineRunner { public static void main(String[] args) { SpringApplication.run(MainApplication.class, args); } public void runRestClient() { RestClient restClient = RestClient.create("https://jsonplaceholder.typicode.com"); List<User> list = restClient.get().uri("/users").retrieve() .body(new ParameterizedTypeReference<List<User>>() { }); for (User u: list) { System.out.println("User id "+u.id()); System.out.println("User id "+u.username()); System.out.println("User id "+u.name()); System.out.println("User id "+u.email()); System.out.println("=================="); } } @Bean public Runnable myRunnable() { return () -> runRestClient(); } @Override public void run(String... args) throws Exception { myRunnable().run(); } }
As you can see from the above code, to consume a REST API with RestClient, you need to follow these steps:
- Create a RestClient instance using the
RestClient.create()
method, specifying the base URL of the API. - Use the
get()
,post()
,put()
, ordelete()
method to create a request builder. - Use the
uri()
method to specify the URI of the resource. - Use the
retrieve()
method to execute the request and obtain a response builder. - Use the
body()
method to specify the desired type of the response. - Call the
toEntity()
ortoFlux()
method to obtain the response object.
By running the application, you should see the list of Users fetched from the GET request:
mvn install spring-boot:run . . . . User id 1 User id Bret User id Leanne Graham User id Sincere@april.biz . . . .
Coding POST, PUT and DELETE methods
The above REST Service allows us only to fetch items with a GET Request. In real world examples you should map all the HTTP methods. For example:
User findById(int id) { return restClient.get() .uri("/users/{id}", id) .retrieve() .body(User.class); } User create(User post) { return restClient.post() .uri("/posts") .contentType(MediaType.APPLICATION_JSON) .body(post) .retrieve() .body(User.class); } User update(Integer id, User post) { return restClient.put() .uri("/users/{id}", id) .contentType(MediaType.APPLICATION_JSON) .body(post) .retrieve() .body(User.class); } void delete(Integer id) { restClient.delete() .uri("/users/{id}", id) .retrieve() .toBodilessEntity(); }
Advanced Rest Client options
You can create a Rest Client with the builder pattern to allow fine-grained customization of your Client. For example, you can add message converters, set Path variables or Headers or include Interceptors or Inizializers.
For example:
RestClient restClient = RestClient.builder() .requestFactory(new SimpleClientHttpRequestFactory()) .messageConverters(converters -> converters.add(new MappingJackson2HttpMessageConverter())) .baseUrl("https://acme.com") .defaultHeader("user_id", "demouser") .requestInterceptor(myCustomInterceptor) .requestInitializer(myCustomInitializer) .build();
Let’s see more in detail the fluent method options:
.requestFactory(new SimpleClientHttpRequestFactory())
: The RestClient instance usesSimpleClientHttpRequestFactory
to generate HTTP requests..messageConverters(converters -> converters.add(new MappingJackson2HttpMessageConverter())):
We are using aMappingJackson2HttpMessageConverter
. This allows, for example, to convert JSON data to and from Java objects using the Jackson library..baseUrl("https://www.acme.com")
: This line defines the base URL for the RestClient, ensuring that all requests made using this client will use that URL..defaultHeader("user_id", "demouser")
: Thee RestClient sets a default header for all requests..requestInterceptor(myCustomInterceptor)
: We are enabling the modification of outgoing requests before executing them. We will need an implementation of the ClientHttpRequestInterceptor interface..requestInitializer(myCustomInitializer)
: The request initializer performs necessary initialization of the request before executing it. We assume that myCustomInitializer is an implementation of the RestTemplateRequestInitializer interface.
For example, here is a sample Interceptor implementation you can use:
// Define custom request interceptor ClientHttpRequestInterceptor myCustomInterceptor = new ClientHttpRequestInterceptor() { @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { // Add custom logic before sending the request System.out.println("Sending request to: " + request.getURI()); // Access and potentially modify request headers or body based on your needs String clientId = request.getHeaders().getFirst("user_id"); System.out.println("Existing client_id header: " + clientId); // Modify existing header or add a new one (optional) request.getHeaders().set("user_id", "modified-client-id"); // Execute the request with potentially modified values return execution.execute(request, body); } };
Conclusion
Spring RestClient is a powerful and easy-to-use tool for consuming REST APIs. It is a great alternative to RestTemplate for Spring Boot applications that require a more concise and functional approach to API consumption.
Source code: https://github.com/fmarchioni/masterspringboot/tree/master/rest/rest-client
To learn more about REST Template check this article: Getting Started with REST Template
Found the article helpful? if so please follow us on Socials