RestClient is a new HTTP client introduced in Spring 6.1 and Spring boot 3.2.
It is a synchronous web client meaning that it waits for the response to arrive before moving futher.

RestClient has a fluent API similar to WebClient, meaning that the methods are called in a flow such as

RestClient.
create().
get().
uri();

Creating RestClient

There are multiple ways to create a RestClient.

1. create()

A RestClient object can be created using its static create() method as shown below

RestClient client = RestClient.create();

2. Builder

Call static builder() method to create a Builder object.
Using this builder object, we can create a RestClient object using its build() method as shown below

Builder builder = RestClient.builder();
RestClient client = builder.build();

With builder, you have options to specify multiple options such as headers, default URL, path variables etc., as shown below

RestClient client = RestClient.builder()
  .baseUrl("https://example.com")
  .defaultUriVariables(Map.of("var", "val"))
  .defaultHeader("Custom-Header", "h123")
  .build();

3. With RestTemplate

RestTemplate is a very popular HTTP client of spring framework and is used in many applications.
Since RestClient is newer, you can create its object using RestTemplate configuration as well to have compatibility with existing codebase.

RestTemplate t = new RestTemplate();
RestClient client = RestClient.create(t);

RestClient HTTP GET request

To execute a GET request using rest client, first step is to create a rest client. Thereafter, follow below steps:

  1. Call get() method to specify HTTP GET request.
  2. Call uri() method to specify the URL at which request will be sent.
  3. Call retrieve() to specify how the response should be collected. That is, only the body of response should the response be stored or the status code and headers are also required with body.
  4. Call body() method to get the response or toEntity(), if the status code and response headers are also required.

Example of RestClient body() method is

RestClient client = RestClient.create();
String response = client.
                  get().
                  uri("http://jsonplaceholder.typicode.com/posts").
                  retrieve().
                  body(String.class);

Below is an example of RestClient HTTP GET request

RestClient client = RestClient.create();
ResponseEntity<String> re = client.
                            get().
                            uri("http://jsonplaceholder.typicode.com/posts").
                            retrieve().
                            toEntity(String.class);
String body = re.getBody();
String statusCode = re.getStatusCode().toString();
String headers = responseEntity.getHeaders().toString();

Note that toEntity() method returns a ResponseEntity object, which wraps the response body, status code and headers.

RestClient HTTP POST request

Similar to GET, RestClient can also be used to execute HTTP POST requests.
The only difference is that instead of get(), use post() method as shown below

RestClient client = RestClient.create();
Post p = new Post(1, 1, "title", "body");
ResponseEntity<Void> r = client.
                         post().
                         uri("https://jsonplaceholder.typicode.com/posts").
                         body(p).
                         retrieve().
                         toBodilessEntity();

For HTTP POST, you need to supply object to be sent in request body using body().
toBodilessEntity() returns a ResponseEntity object without response body.

RestClient HTTP PUT request

HTTP PUT method is used to update a resource and you can do it easily with RestClient.
Simply use put() method followed by uri() method and then body() method with the object to be updated as argument.
retrieve() method is used to configure how you want to handle the response.
If you are interested in body of response, then use body() after retrieve().
If you are interested in response body, headers and response code as well, then use toEntity() after retrieve().
If you do not want to use response, then use toBodilessEntity() after retrieve().

RestClient client = RestClient.create();
Post post = new Post(5, 1, "title", "desc");
ResponseEntity<Void> re = client.
                         put().
                         uri("https://jsonplaceholder.typicode.com/posts/1").
                         body(post).
                         retrieve().
                         toBodilessEntity();

RestClient HTTP DELETE method

HTTP DELETE is used to delete a resource from the server.
To execute HTTP DELETE, use delete() method of RestClient as shown below

ResponseEntity<Void> re = client.
                          delete().
                          uri("https://jsonplaceholder.typicode.com/posts/1").
                          retrieve().
                          toBodilessEntity();

Note that you cannot use body() with delete() since there is no request body with HTTP DELETE method.

Error Handling

RestClient will throw RestClientException, which is an unchecked exception when it encounters 4xx or 5xx status code in response.

You can customize error handling based on specific error codes using its onStatus() method.
onStatus() accepts two arguments :
I. A predicate to evaluate if a condition is true or false.
II. An ErrorHandler object, which is a functional interface having handle() method, that accepts request and response object arguments. Example,

RestClient client = RestClient.create();
ResponseEntity<String> re = client.
                            get(). 
                            uri("https://jsonplaceholder.typicode.com/posts/{id}", 1).
			    retrieve().
			    onStatus(s -> s.value() == 404, (req, res)-> {
			      throw new RuntimeException("Resource not found");
			    }).
			    onStatus(s -> s.value() >= 500, (req, res)-> {
			      throw new RuntimeException("Server Error");
			    }).
			    toEntity(String.class);

exchange() method

If you want direct access to request and response objects, then use exchange() method after get() or post() instead of body() method.

When using exchange(), we need to handle status code ourselves as shown in below example.

exchange() method is useful in scenarios, where we need to handle response data differently based on different status codes or conditions.

RestClient client = RestClient.create();
ObjectMapper m = new ObjectMapper();
List<Post> posts = client.
                   get().
                   uri("https://jsonplaceholder.typicode.com/posts").
                   exchange((rq, rs) -> {
                     if (rs.getStatusCode().isSameCodeAs(HttpStatusCode.valueOf(204))) {
                       throw new RuntimeException("URL invalid");
                     } else if (rs.getStatusCode().isSameCodeAs(HttpStatusCode.valueOf(200))) {
                       return m.readValue(rs.getBody(), new TypeReference<>() {});
                     } else {
                       throw new RuntimeException("Error");
                     }
                   });

In above example, if the status code is 200, then we are converting response body to a list of objects using Jackson ObjectMapper.

URL parameters

You can specify URL parameters separately instead of hardcoding them into the URL itself.
Simply supply placeholders for parameters in URL inside curly braces and provide their values as arguments to uri() method.

RestClient will replace the placeholders with their values at runtime.
Second argument to uri() is a var-args, so you can provide any number of values and they will be replaced in the same order.

RestClient client = RestClient.create();
ResponseEntity<String> re = client.
                            get().
                            uri("https://jsonplaceholder.typicode.com/posts/{id}/{uid}", 1,5).
                            retrieve().
                            toEntity(String.class);

In above example, the actual URL will translate to https://jsonplaceholder.typicode.com/posts/1/5.

Conclusion

RestClient is a new HTTP client introduced in Spring 6.1 and Spring boot 3.2., which is very easy to initialize and use.
In this article, we saw how to create it in different ways, execute HTTP requests and handle response and errors with it.