Spring Rest Client API made simple

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:

  1. Create a RestClient instance using the RestClient.create() method, specifying the base URL of the API.
  2. Use the get()post()put(), or delete() method to create a request builder.
  3. Use the uri() method to specify the URI of the resource.
  4. Use the retrieve() method to execute the request and obtain a response builder.
  5. Use the body() method to specify the desired type of the response.
  6. Call the toEntity() or toFlux() 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:

  1. .requestFactory(new SimpleClientHttpRequestFactory()): The RestClient instance uses SimpleClientHttpRequestFactory to generate HTTP requests.
  2. .messageConverters(converters -> converters.add(new MappingJackson2HttpMessageConverter())): We are using a MappingJackson2HttpMessageConverter. This allows, for example, to convert JSON data to and from Java objects using the Jackson library.
  3. .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.
  4. .defaultHeader("user_id", "demouser"): Thee RestClient sets a default header for all requests.
  5. .requestInterceptor(myCustomInterceptor): We are enabling the modification of outgoing requests before executing them. We will need an implementation of the ClientHttpRequestInterceptor interface.
  6. .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
Twitter Icon       Facebook Icon       LinkedIn Icon       Mastodon Icon