Type Here to Get Search Results !

RestClient in Spring Boot | Synchronous Communication with Examples

0

RestClient in Spring Boot: Synchronous Communication

Learn how OrderService can call ProductService using the modern, fluent RestClient API, with practical Java examples and request/response handling.

Spring Framework 6+Spring Boot 3+MicroservicesRestClientJavaInterview
Spring Boot • Microservices

RestClient
Synchronous Communication

Call another service using a readable, fluent HTTP client API in Spring applications.

REST

What You'll Learn

Limitations of RestTemplate
What is RestClient?
OrderService and ProductService
Fluent API and method chaining
Handling HTTP errors
retrieve() versus exchange()
Adding request interceptors
Common interview questions
PDF revision notes
Table of Contents

1. Introduction to RestClient

RestClient is a synchronous HTTP client introduced in Spring Framework 6.1. It provides a modern fluent API for making HTTP requests from Spring applications. The supplied notes describe it as the alternative to the older RestTemplate API.

In this example, OrderService calls ProductService. The client sends an HTTP request and waits for the target service's response before continuing. This is synchronous, or blocking, communication.

Request Flow
Client: OrderService (port 8081) | | HTTP request using RestClient v Server: ProductService (port 8082) | | HTTP response v OrderService continues processing

2. Limitations of RestTemplate

The supplied PDF lists these limitations of RestTemplate:

Why consider RestClient?

  • RestTemplate has many overloaded methods, which can be harder to remember and maintain.
  • It was designed before patterns and tools such as retry and circuit breakers became common, so extending usage can feel less convenient.
  • RestTemplate is in maintenance mode: it receives bug fixes, but it is not the focus for new features.

RestClient provides a more fluent, readable way to express request steps. It is still synchronous and blocking; it is not a reactive or non-blocking client.

3. Setting Up Two Microservices

For the example, run two Spring Boot applications on different ports. OrderService is the calling service, and ProductService exposes a product endpoint.

OrderService — application.properties

Properties
server.port=8081

ProductService — application.properties

Properties
server.port=8082

ProductService controller

Java
@RestController
@RequestMapping("/products")
public class ProductController {

    @GetMapping("/{id}")
    public String getProduct(@PathVariable String id) {
        return "Product fetched with id: " + id;
    }
}

Configure a RestClient bean

Java
@Configuration
public class AppConfig {

    @Bean
    public RestClient restClientInstance() {
        return RestClient.create();
    }
}

Inject this bean into OrderService and use it to call the ProductService endpoint.

4. What Is a Fluent API?

A fluent API allows method calls to be chained together. Each step returns an object that exposes the operations available at the next step. In RestClient, a request often follows this sequence:

Typical Fluent API Flow
restClient .get() // choose HTTP method .uri(...) // set target URL .retrieve() // execute and prepare to read response .body(...) // convert response body

The sequence matters because each method represents a different stage of preparing or handling the HTTP request.

5. GET Request Using RestClient

Use GET when OrderService needs to read data from ProductService. The example below requests a product by ID and reads the response as a String.

Java
RestClient restClient = RestClient.create();

String response = restClient
        .get()
        .uri("http://localhost:8082/products/" + id)
        .accept(MediaType.APPLICATION_JSON)
        .header("X-Custom-Header", "xyz")
        .retrieve()
        .body(String.class);

accept() sets the media type the client can accept, and header() adds a request header. retrieve() starts response handling, while body(String.class) converts the response body to a String.

Example Output
Product fetched with id: 1

6. POST Request Using RestClient

Use POST when the client needs to send data to the target service, for example to create a product. The supplied notes use a ProductEntity object as the request body.

Java
RestClient restClient = RestClient.create();

ResponseEntity<ProductEntity> response = restClient
        .post()
        .uri("http://localhost:8082/products/create")
        .accept(MediaType.APPLICATION_JSON)
        .header("Content-Type", "application/json")
        .body(new ProductEntity("Ice-cream"))
        .retrieve()
        .toEntity(ProductEntity.class);

ProductEntity responseBody = response.getBody();

body() supplies the object to send. toEntity() returns a ResponseEntity containing response details and the converted body.

7. DELETE Request Using RestClient

Use DELETE to request removal of a resource. The example retrieves the status information without expecting a response body.

Java
RestClient restClient = RestClient.create();

ResponseEntity<Void> response = restClient
        .delete()
        .uri("http://localhost:8082/products/" + id)
        .retrieve()
        .toBodilessEntity();

HttpStatusCode deletionStatus = response.getStatusCode();

toBodilessEntity() is useful when the response status and headers matter but the body does not.

8. Exception Handling with retrieve()

The supplied notes demonstrate customizing status handling with onStatus(). This allows the application to react differently to client errors (4xx) and server errors (5xx).

Java — illustrative pattern
String response = restClient
        .get()
        .uri("http://localhost:8082/products/" + id)
        .retrieve()
        .onStatus(
            status -> status.is4xxClientError(),
            (request, clientResponse) -> {
                throw new MyCustomException("Invalid request passed");
            }
        )
        .body(String.class);

Important idea

Use onStatus() when you want to define how selected HTTP response statuses should be handled while using retrieve(). The exception classes in the example are application-specific and must exist in your project.

9. Using exchange() for More Control

The PDF explains that exchange() can be used when you want direct control over response processing and exception handling instead of relying on the usual retrieve() response flow.

Java — illustrative pattern
String responseBody = restClient
        .get()
        .uri("http://localhost:8082/products/" + id)
        .exchange((request, response) -> {
            if (response.getStatusCode().is4xxClientError()) {
                throw new MyCustomException("Invalid request passed");
            } else if (response.getStatusCode().is5xxServerError()) {
                throw new IllegalStateException("Something went wrong at server");
            }

            return StreamUtils.copyToString(
                response.getBody(),
                StandardCharsets.UTF_8
            );
        });

With exchange(), your callback receives the request and response, so you can inspect the status and decide how to map the result. Make sure your project has the required imports and custom exception classes.

10. Adding Request Interceptors

An interceptor can inspect or modify a request before it is sent. In the supplied example, the interceptor adds an X-Custom-Header header to the outgoing request.

Custom interceptor

Java
public class MyCustomRequestInterceptor
        implements ClientHttpRequestInterceptor {

    @Override
    public ClientHttpResponse intercept(
            HttpRequest request,
            byte[] body,
            ClientHttpRequestExecution execution
    ) throws IOException {

        request.getHeaders().add("X-Custom-Header", "myvalue");
        return execution.execute(request, body);
    }
}

Register the interceptor

Java
@Configuration
public class AppConfig {

    @Bean
    public ClientHttpRequestInterceptor customRequestInterceptor() {
        return new MyCustomRequestInterceptor();
    }

    @Bean
    public RestClient restClientInstance(
            ClientHttpRequestInterceptor customRequestInterceptor) {
        return RestClient.builder()
                .requestInterceptor(customRequestInterceptor)
                .build();
    }
}

When ProductService receives the request, its headers include X-Custom-Header: myvalue. The interceptor runs as part of request execution.

11. Best Practices and Summary

Remember these points

  • Use RestClient for synchronous HTTP calls in Spring applications.
  • Use the fluent chain to make request setup easy to read.
  • Choose GET, POST and DELETE according to the operation you need.
  • Use retrieve() for the common response-handling flow.
  • Use onStatus() to customize handling of selected HTTP statuses.
  • Use exchange() when you need direct control over the response.
  • Register an interceptor when requests need shared headers or other common processing.
MethodTypical purposeExample in this article
get()Read a resourceFetch product by ID
post()Send data / create a resourceSend ProductEntity
delete()Request resource deletionDelete product by ID
retrieve()Use the standard response-spec flowRead body or response entity
exchange()Handle request and response directlyCustom status handling
requestInterceptor()Apply common request logicAdd a custom header

12. RestClient Interview Questions

1. What is RestClient in Spring?
RestClient is a synchronous HTTP client with a fluent API for making requests and handling responses in Spring applications.
2. Is RestClient synchronous or asynchronous?
RestClient is synchronous and blocking. The calling thread waits for the HTTP response.
3. What does fluent API mean?
It means chaining method calls, where each step exposes the operations available for the next step.
4. What is the difference between retrieve() and exchange()?
retrieve() offers a standard response-handling flow. exchange() gives the callback direct access to the request and response for custom handling.
5. Why use a request interceptor?
An interceptor can apply common request processing, such as adding a custom header before the request is sent.
6. What is the role of toBodilessEntity()?
It returns response metadata, such as status and headers, without mapping a response body.

13. Frequently Asked Questions

Is RestClient blocking?

Yes. RestClient is synchronous; the caller waits for the response.

Which Spring versions include RestClient?

RestClient was introduced in Spring Framework 6.1. Spring Boot 3.2 and later versions align with Spring Framework 6.1 or newer.

Can I add custom headers?

Yes. You can set headers for an individual request or use a request interceptor for common headers.

When should I use exchange()?

Use exchange() when you need direct control over the response and custom response mapping or error handling.

Conclusion

RestClient gives Spring developers a modern fluent API for synchronous HTTP communication. By understanding request chaining, response handling, status checks, exchange() and interceptors, you can build clearer service-to-service calls between applications such as OrderService and ProductService.

Post a Comment

0 Comments