<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: pallavi kamble</title>
    <description>The latest articles on DEV Community by pallavi kamble (@pallavi_kamble_07).</description>
    <link>https://dev.to/pallavi_kamble_07</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3501819%2F82affdb0-c685-42dc-9239-900dc186d31c.jpg</url>
      <title>DEV Community: pallavi kamble</title>
      <link>https://dev.to/pallavi_kamble_07</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pallavi_kamble_07"/>
    <language>en</language>
    <item>
      <title>🚀 Mastering Asynchronous API Calls with Spring WebClient</title>
      <dc:creator>pallavi kamble</dc:creator>
      <pubDate>Sat, 11 Oct 2025 12:34:47 +0000</pubDate>
      <link>https://dev.to/pallavi_kamble_07/mastering-asynchronous-api-calls-with-spring-webclient-28p5</link>
      <guid>https://dev.to/pallavi_kamble_07/mastering-asynchronous-api-calls-with-spring-webclient-28p5</guid>
      <description>&lt;p&gt;In modern microservices, non-blocking asynchronous communication is critical to building scalable and resilient systems. Spring WebClient, part of Spring WebFlux, makes it easy to perform asynchronous HTTP calls, replacing the older, blocking RestTemplate.&lt;/p&gt;

&lt;p&gt;This blog covers how to use WebClient for asynchronous communication, including examples, chaining, parallel calls, and error handling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is WebClient?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;WebClient is a reactive, non-blocking HTTP client. Unlike RestTemplate, it doesn’t block the thread while waiting for a response. Instead, it returns reactive types:&lt;/p&gt;

&lt;p&gt;Mono – Represents 0 or 1 element.&lt;/p&gt;

&lt;p&gt;Flux – Represents 0 or N elements.&lt;/p&gt;

&lt;p&gt;This enables high throughput and efficient use of resources, especially when calling multiple microservices concurrently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Commonly Used Methods in Mono and Flux&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. flatMap →&lt;/strong&gt; Chain Service Calls&lt;br&gt;
flatMap is used to call another asynchronous method and flatten the result into the same pipeline. This is the most important method when chaining multiple WebClient calls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. map →&lt;/strong&gt; Transform Response&lt;br&gt;
map is used to transform the value inside a Mono or Flux synchronously.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. zip →&lt;/strong&gt; Combine Multiple Responses&lt;br&gt;
zip is used when responses from multiple services need to be combined. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. onErrorResume →&lt;/strong&gt; Handle Failures Gracefully &lt;br&gt;
onErrorResume helps in applying fallback logic when a failure occurs. Often used for resilience patterns, like returning fallback responses or calling a backup service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. doOnNext →&lt;/strong&gt; Logging and Debugging&lt;br&gt;
doOnNext executes side-effects whenever a new element is emitted, without modifying the actual data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Benefits of Using Mono &amp;amp; Flux&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;1. Non-blocking:&lt;/strong&gt; Your app can handle many requests without blocking threads.&lt;br&gt;
&lt;strong&gt;2. Backpressure support:&lt;/strong&gt; Consumers can control the rate at which data is emitted.&lt;br&gt;
&lt;strong&gt;3. Composability:&lt;/strong&gt; Easy to chain operations like map(), filter(), flatMap().&lt;br&gt;
&lt;strong&gt;4. Error handling:&lt;/strong&gt; Clean handling of errors without try/catch blocks.&lt;br&gt;
&lt;strong&gt;5. Streaming:&lt;/strong&gt; Ideal for reactive APIs, WebSockets, or real-time data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Blocking vs. Non-blocking API Calls&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Blocking API Calls:&lt;/strong&gt;&lt;br&gt;
The caller waits for the response before moving on.&lt;br&gt;
The thread that makes the call is blocked until the operation completes.&lt;br&gt;
Easy to understand and implement but not scalable for high-load systems.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    @GetMapping("/blocking/product/{id}")
    public ProductResponse getProductBlocking(@PathVariable Long id) {
        return WebClient.create("http://localhost:8081/products")
                .get()
                .uri("/{id}", id)
                .retrieve()
                .bodyToMono(ProductResponse.class)
                .block();
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Non-blocking API Calls:&lt;/strong&gt;&lt;br&gt;
The caller does not wait, it continues execution immediately.&lt;br&gt;
The response is handled asynchronously when it arrives.&lt;br&gt;
More scalable, efficient, and ideal for reactive programming.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@GetMapping("/nonblocking/product/{id}")
public Mono&amp;lt;ProductResponse&amp;gt; getProductNonBlocking(@PathVariable Long id) {
    return WebClient.create("http://localhost:8081/products")
            .get()
            .uri("/{id}", id)
            .retrieve()
            .bodyToMono(ProductResponse.class);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;HTTP Methods in WebClient&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.    GET Method:&lt;/strong&gt;&lt;br&gt;
GET API to fetches either a collection of resources or a singular resource.&lt;br&gt;
Example 1: Use &lt;strong&gt;Mono&lt;/strong&gt; to fetch only a single resource.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public Mono&amp;lt;ProductResponse&amp;gt; getProductById(Long productId) {
        return productClient.get()
                .uri("/{id}", productId)
                .retrieve()
                .bodyToMono(ProductResponse.class);
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example 2: Use &lt;strong&gt;Flux&lt;/strong&gt; to fetch a collection of resources.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; public Flux&amp;lt;ProductResponse&amp;gt; getAllProducts() {
        return productClient.get()
                .uri("/")
                .retrieve()
                .bodyToFlux(ProductResponse.class);
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2.    POST Method:&lt;/strong&gt;&lt;br&gt;
Use to send data to the server to create a new resource.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public Mono&amp;lt;ProductResponse&amp;gt; createProduct(ProductRequest productRequest) {
        return productClient.post()
                .uri("/create")
                .bodyValue(productRequest)
                .retrieve()
                .bodyToMono(ProductResponse.class);
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3.    PUT Method:&lt;/strong&gt;&lt;br&gt;
Use to replace an existing resource.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  public Mono&amp;lt;ProductResponse&amp;gt; updateProduct(Long productId, ProductRequest productRequest) {
        return productClient.put()
                .uri("/update/{id}", productId)
                .bodyValue(productRequest)
                .retrieve()
                .bodyToMono(ProductResponse.class);
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4.    DELETE Method:&lt;/strong&gt;&lt;br&gt;
Use to delete a resource.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public Mono&amp;lt;Void&amp;gt; deleteProduct(Long productId) {
        return productClient.delete()
                .uri("/delete/{id}", productId)
                .retrieve()
                .bodyToMono(Void.class);
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5.    Patch Method:&lt;/strong&gt;&lt;br&gt;
Use to update only part of a resource.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; public Mono&amp;lt;ProductResponse&amp;gt; reduceStock(Long productId, int quantity) {
        return productClient.patch()
                .uri(uriBuilder -&amp;gt; uriBuilder
                        .path("/{id}/reduceStock")
                        .queryParam("stock", quantity)
                        .build(productId))
                .retrieve()
                .bodyToMono(ProductResponse.class);
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Chaining, Composition, and Parallel Calls&lt;/strong&gt;&lt;br&gt;
One of the most powerful aspects of reactive programming in Spring WebFlux is the ability to chain operations, compose streams, and execute API calls in parallel.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.    Chaining:&lt;/strong&gt; Sequential, dependent operations.&lt;br&gt;
Chaining allows to apply multiple operations sequentially on a reactive stream Transformations, filters, or side effects can be performed without blocking the thread.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public Mono&amp;lt;String&amp;gt; chainingProductAndCustomer(Long productId, Long customerId) {
    return getCustomerById(customerId)
            .flatMap(customer -&amp;gt;
                    getProductById(productId)
                            .map(product -&amp;gt; 
                                "Customer: " + customer.getCustomerName() +
                                ", Product: " + product.getProductName()
                            )
            );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Explanation:&lt;br&gt;
• getCustomerById() fetches a customer.&lt;br&gt;
• Only after customer is fetched, getProductById() is called.&lt;br&gt;
• Result combines customer and product info.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.    Composition:&lt;/strong&gt; Dependent operations composed together.&lt;br&gt;
Composition is about combining multiple reactive streams into one. This is particularly useful when making multiple API calls that either depend on each other or need to be merged.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public Mono&amp;lt;String&amp;gt; compositionExample(Long orderId) {
    return getOrderById(orderId)
            .flatMap(order -&amp;gt; getProductById(order.getProductId())
                    .flatMap(product -&amp;gt; getCustomerById(order.getCustomerId())
                            .map(customer -&amp;gt; 
                                "Order: " + order.getOrderId() +
                                ", Customer: " + customer.getCustomerName() +
                                ", Product: " + product.getProductName()
                            )
                    )
            );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Explanation:&lt;br&gt;
• You fetch order, then based on order details, fetch product and customer.&lt;br&gt;
• Uses nested flatMap, combining multiple dependent calls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.    Flux Parallel Calls:&lt;/strong&gt; Faster than chaining when calls don’t depend on each other.&lt;br&gt;
When working with collections of IDs or items, Flux provides a convenient way to perform multiple parallel requests and merge their results.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public Mono&amp;lt;String&amp;gt; parallelExample(Long customerId, Long productId) {
    Mono&amp;lt;String&amp;gt; customerMono = getCustomerById(customerId).map(c -&amp;gt; "Customer: " + c.getCustomerName());
    Mono&amp;lt;String&amp;gt; productMono = getProductById(productId).map(p -&amp;gt; "Product: " + p.getProductName());

    return Mono.zip(customerMono, productMono)
            .map(tuple -&amp;gt; tuple.getT1() + " | " + tuple.getT2());
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Explanation:&lt;br&gt;
• getCustomerById() and getProductById() are independent → run in parallel.&lt;br&gt;
• Mono.zip() combines results when both complete.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exception Handling&lt;/strong&gt;&lt;br&gt;
In asynchronous communication with WebClient, exceptions are not managed using traditional try-catch blocks. Instead, they flow through the reactive pipeline (Mono or Flux). Operators such as onStatus, onErrorResume, onErrorReturn, and doOnError are used to handle them in a non-blocking manner.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.    Handling HTTP Error Status with onStatus()&lt;/strong&gt;&lt;br&gt;
When using retrieve(), WebClient automatically maps 2xx responses to the response body. Error statuses such as 4xx and 5xx require explicit handling through onStatus.&lt;br&gt;
&lt;strong&gt;onStatus&lt;/strong&gt; checks for error codes. Instead of returning a normal response, it maps them to a custom exception.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.    Fallback Mechanisms&lt;/strong&gt;&lt;br&gt;
Instead of failing the entire pipeline, a fallback strategy can be applied.&lt;br&gt;
The operator onErrorReturn provides a static default response, while onErrorResume allows dynamic recovery, such as calling another service or applying conditional logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.    Timeout and Retry&lt;/strong&gt;&lt;br&gt;
Resilience in distributed systems often requires timeouts and retries. WebClient supports this directly in the reactive pipeline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.    Centralized Error Handling&lt;/strong&gt;&lt;br&gt;
For consistent behavior across the application, exceptions can be handled globally using @ControllerAdvice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example of Asynchronous Communication&lt;/strong&gt;&lt;br&gt;
To demonstrate how reactive and asynchronous communication works in microservices, let’s build a simple e-commerce system using WebClient in Spring Boot.&lt;br&gt;
The application consists of three services — Product Service, Customer Service, and Order Service — communicating asynchronously using Spring WebFlux and WebClient.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Product Service –&lt;/strong&gt; Manages product details and stock.&lt;br&gt;
Handles CRUD operations for products.&lt;br&gt;
Exposes endpoints for GET, POST, PUT, PATCH, and DELETE.&lt;br&gt;
The PATCH endpoint updates stock after order placement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customer Service –&lt;/strong&gt; Manages customer information.&lt;br&gt;
Handles customer data CRUD operations.&lt;br&gt;
Independent service used by Order Service to validate customers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Order Service –&lt;/strong&gt; Places orders and communicates with both Product and Customer services.&lt;br&gt;
The core service demonstrating async communication.&lt;br&gt;
Uses WebClient to call Product and Customer services.&lt;br&gt;
Includes examples of chaining, composition, and parallel calls.&lt;/p&gt;

&lt;p&gt;In Order Service :&lt;/p&gt;

&lt;p&gt;Defining as a Spring Bean for reuse.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Configuration
public class WebClientConfig {

        @Bean
    public WebClient productWebClient() {
        HttpClient httpClient = HttpClient.create().option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
                .responseTimeout(Duration.ofSeconds(5))
                .doOnConnected(conn -&amp;gt; conn.addHandlerLast(new ReadTimeoutHandler(5, TimeUnit.SECONDS))
                        .addHandlerLast(new WriteTimeoutHandler(5, TimeUnit.SECONDS)));

        return WebClient.builder().baseUrl("http://localhost:8081/products")
                .clientConnector(new ReactorClientHttpConnector(httpClient)).build();
    }

    @Bean
    public WebClient customerWebClient() {
        HttpClient httpClient = HttpClient.create().option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000) 
                .responseTimeout(Duration.ofSeconds(5))
                .doOnConnected(conn -&amp;gt; conn.addHandlerLast(new ReadTimeoutHandler(5, TimeUnit.SECONDS))
                        .addHandlerLast(new WriteTimeoutHandler(5, TimeUnit.SECONDS)));

        return WebClient.builder().baseUrl("http://localhost:8082/customer")
                .clientConnector(new ReactorClientHttpConnector(httpClient)).build();
    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Creating DTO Classes.&lt;/p&gt;

&lt;p&gt;ProductResponse&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Data
public class ProductResponse {

    private Long productId;
    private String productName;
    private String productCategory;
    private Double productPrice;
    private Integer stock;

}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CustomerResponse&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Data
public class CustomerResponse {

    private Long customerId;
    private String customerName;
    private String email;
    private String address;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Creating Service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Service
public class OrderService {

    private final OrderRepository orderRepository;
    private final WebClient customerClient;
    private final WebClient productClient;

    @Autowired
    public OrderService(OrderRepository orderRepository, WebClient customerWebClient, WebClient productWebClient) {
        this.orderRepository = orderRepository;
        this.customerClient = customerWebClient;
        this.productClient = productWebClient;
    }

    // Get Customer by ID
    public Mono&amp;lt;CustomerResponse&amp;gt; getCustomerById(Long customerId) {
        return customerClient.get()
                .uri("/{id}", customerId)
                .retrieve()
                .onStatus(status -&amp;gt; status.is4xxClientError(), clientResponse -&amp;gt; 
                    clientResponse.bodyToMono(String.class)
                                  .flatMap(body -&amp;gt; Mono.error(new RuntimeException("Customer not found")))
                )
                .onStatus(status -&amp;gt; status.is5xxServerError(), clientResponse -&amp;gt; 
                    clientResponse.bodyToMono(String.class)
                                  .flatMap(body -&amp;gt; Mono.error(new RuntimeException("Customer service error")))
                )
                .bodyToMono(CustomerResponse.class)
                .timeout(Duration.ofSeconds(5))
                .doOnNext(c -&amp;gt; System.out.println("Fetched customer: " + c))
                .doOnError(e -&amp;gt; System.out.println("Error fetching customer: " + e.getMessage()));

    }

    // Get Product by ID
    public Mono&amp;lt;ProductResponse&amp;gt; getProductById(Long productId) {
        return productClient.get()
                .uri("/{id}", productId)
                .retrieve()
                .onStatus(status -&amp;gt; status.is4xxClientError(), clientResponse -&amp;gt; 
                    clientResponse.bodyToMono(String.class)
                                  .flatMap(body -&amp;gt; Mono.error(new RuntimeException("Product not found")))
                )
                .onStatus(status -&amp;gt; status.is5xxServerError(), clientResponse -&amp;gt; 
                    clientResponse.bodyToMono(String.class)
                                  .flatMap(body -&amp;gt; Mono.error(new RuntimeException("Product service error")))
                )
                .bodyToMono(ProductResponse.class)
                .timeout(Duration.ofSeconds(5))
                .onErrorResume(e -&amp;gt; {
                    System.out.println("Fallback triggered for product: " + e.getMessage());
                    ProductResponse fallback = new ProductResponse();
                    fallback.setProductId(productId);
                    fallback.setProductName("Fallback Product");
                    fallback.setStock(0);
                    fallback.setProductPrice(0.0);
                    return Mono.just(fallback); 
                });         
    }

    // Reduce stock
    public Mono&amp;lt;ProductResponse&amp;gt; reduceStock(Long productId, int stock) {
        return productClient.patch()
                .uri(uriBuilder -&amp;gt; uriBuilder
                        .path("/{id}/reduceStock")
                        .queryParam("stock", stock)
                        .build(productId))
                .retrieve()
                .onStatus(status -&amp;gt; status.is4xxClientError(), clientResponse -&amp;gt; 
                    clientResponse.bodyToMono(String.class)
                                  .flatMap(body -&amp;gt; Mono.error(new RuntimeException("Client error: " + body)))
                )
                .onStatus(status -&amp;gt; status.is5xxServerError(), clientResponse -&amp;gt; 
                    clientResponse.bodyToMono(String.class)
                                  .flatMap(body -&amp;gt; Mono.error(new RuntimeException("Server error: " + body)))
                )
                .bodyToMono(ProductResponse.class)
                .timeout(Duration.ofSeconds(5))
                .doOnNext(p -&amp;gt; System.out.println("Stock reduced: " + p))
                .doOnError(e -&amp;gt; System.out.println("Error calling reduceStock: " + e.getMessage()));
    }


    // Place Order
    public Mono&amp;lt;Order&amp;gt; placeOrder(Order order) {
        return getCustomerById(order.getCustomerId())
                .flatMap(customer -&amp;gt; 
                    getProductById(order.getProductId())
                        .flatMap(product -&amp;gt; {
                            if(product.getStock() &amp;lt; order.getQuantity()) {
                                return Mono.error(new RuntimeException("Insuffient Stock...!!!"));
                            }
                            return reduceStock(order.getProductId(), order.getQuantity())
                                    .flatMap(updatedProduct -&amp;gt; {
                                        order.setTotalPrice(order.getQuantity() * product.getProductPrice());
                                        order.setCreatedAt(LocalDateTime.now());
                                        return orderRepository.save(order)  ;   
                                    });
                        }) 
                )
        .doOnError(e -&amp;gt; System.out.println("Error in placing order: " + e.getMessage()));
    }

}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create Controller.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@RestController
@RequestMapping("/order")
public class OrderController {

    private final OrderService orderService;

    @Autowired
    public OrderController(OrderService orderService) {
        this.orderService = orderService;
    }

    @PostMapping("/create")
    public Mono&amp;lt;Order&amp;gt; placeOrder(@RequestBody Order order) {
        // Return the reactive stream directly
        return orderService.placeOrder(order);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Curl:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl --location 'http://localhost:8083/order/create' \
--header 'Content-Type: application/json' \
--data '{
  "customerId": 2,
  "productId": 2,
  "quantity": 1
}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br&gt;
WebClient in Spring Boot offers a powerful and reactive way to handle inter-service communication. Through this blog, we explored configuration, asynchronous calls, chaining, composition, and error handling with Mono and Flux. The example project demonstrated how multiple services can interact efficiently in a non-blocking manner, making WebClient an ideal choice for modern microservice communication.&lt;/p&gt;

</description>
      <category>springboot</category>
      <category>java</category>
      <category>microservices</category>
      <category>api</category>
    </item>
    <item>
      <title>🚀 Mastering Synchronous API Calls with Spring WebClient</title>
      <dc:creator>pallavi kamble</dc:creator>
      <pubDate>Mon, 06 Oct 2025 16:26:10 +0000</pubDate>
      <link>https://dev.to/pallavi_kamble_07/mastering-synchronous-api-calls-with-spring-webclient-18om</link>
      <guid>https://dev.to/pallavi_kamble_07/mastering-synchronous-api-calls-with-spring-webclient-18om</guid>
      <description>&lt;p&gt;In modern microservices architecture, services frequently need to communicate with each other or with external APIs over HTTP. Traditionally, developers relied on RestTemplate for such calls, but with the rise of reactive programming and the need for better scalability, Spring introduced WebClient as part of the Spring WebFlux module.&lt;/p&gt;

&lt;p&gt;WebClient is a non-blocking, reactive HTTP client that supports both synchronous and asynchronous communication. It is lightweight, efficient, and designed to handle high-concurrency scenarios, making it the preferred choice for building resilient and scalable microservices.&lt;/p&gt;

&lt;p&gt;🔹 &lt;strong&gt;Features of WebClient in Spring Boot&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Non-blocking &amp;amp; Reactive –&lt;/strong&gt; Built on Project Reactor, it supports asynchronous and non-blocking communication, improving scalability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Supports All HTTP Methods –&lt;/strong&gt; Works with GET, POST, PUT, DELETE, PATCH, and more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flexible Response Handling –&lt;/strong&gt; Convert responses into Mono (single object) or Flux (multiple objects/streams).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customizable Requests –&lt;/strong&gt; Easy to add headers, query parameters, cookies, and authentication tokens.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Error Handling –&lt;/strong&gt; Provides built-in error handling for 4xx/5xx and customizable handling with .onStatus().&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lightweight &amp;amp; Future-Proof –&lt;/strong&gt; Official replacement for RestTemplate, designed for cloud-native applications.&lt;/p&gt;

&lt;p&gt;🔹&lt;strong&gt;Reactive Types – Mono and Flux&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When using WebClient, the HTTP response is not returned immediately like a traditional call. Instead, it is wrapped in a reactive type which represents a pipeline of data that will be available in the future.&lt;/p&gt;

&lt;p&gt;To actually read the response body, we use .retrieve(), which triggers the HTTP call and prepares the response for conversion into Mono or Flux.&lt;/p&gt;

&lt;p&gt;Spring WebFlux uses Project Reactor, which provides two main reactive types:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mono – Single or No Result&lt;/strong&gt;&lt;br&gt;
Represents 0 or 1 item.&lt;br&gt;
Use when the API returns a single object or might be empty.&lt;br&gt;
Examples: fetching a single customer, saving an entity, deleting by ID.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flux – Multiple or Stream of Results&lt;/strong&gt;&lt;br&gt;
Represents 0 to N items.&lt;br&gt;
Use when the API returns a list of items or a continuous stream.&lt;br&gt;
Examples: fetching all customers, streaming logs, Server-Sent Events.&lt;/p&gt;

&lt;p&gt;🔹 &lt;strong&gt;WebClient Configuration&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add Dependency
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;spring-boot-starter-webflux&amp;lt;/artifactId&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Defining as a Spring Bean for reuse.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Configuration
public class WebClientConfig {
    @Bean
    public WebClient.Builder webClientBuilder() {
        return WebClient.builder();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Inject Webclient
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Service
public class BookingService {

    private final WebClient webClient;

    @Autowired
    public BookingService(WebClient.Builder webClientBuilder) {
         this.webClient = webClientBuilder.baseUrl("http://localhost:8081").build(); 
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;🔹 &lt;strong&gt;GET API Example&lt;/strong&gt;&lt;br&gt;
GET API is use to fetch either a collection of resources or a singular resource&lt;/p&gt;

&lt;p&gt;• HTTP GET /hotel: List of Hotel as Flux&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;     public List&amp;lt;HotelResponse&amp;gt; getAllHotels() {
         return webClient
                    .get()  
                    .uri("/hotel")
                    .retrieve()
                    .bodyToFlux(HotelResponse.class)
                    .collectList()
                    .block();
      }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;• HTTP GET /hotel/{id}: single hotel by id as Mono&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HotelResponse hotel = webClient
                .get()
                .uri("hotel/{id}", booking.getHotelId())
                .retrieve()
                .bodyToMono(HotelResponse.class)
                .block();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🔹 &lt;strong&gt;PUT API Example&lt;/strong&gt;&lt;br&gt;
PUT API is commonly used for updating a resource.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;webClient.put()
                    .uri("/hotel/{id}/update-rooms?bookedRooms={count}",
                            booking.getHotelId(), booking.getNoOfRoomsBooked())
                    .retrieve()
                    .bodyToMono(Void.class)
                    .block();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🔹 &lt;strong&gt;Exception Handling in WebClient&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When working with microservices, one service often calls another over HTTP. But what happens if the target service is down, or returns an error (like 404 or 500)? Without proper handling, users might see raw stack traces or cryptic messages.&lt;/p&gt;

&lt;p&gt;With Spring WebClient, we can gracefully handle these errors using .onStatus() and @ControllerAdvice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Error Handling in WebClient&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;.onStatus()&lt;/strong&gt; : handle remote service failures.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    HotelResponse hotel = webClient
                .get()
                .uri("hotel/{id}", booking.getHotelId())
                .retrieve()
                .onStatus(
                          status -&amp;gt; status.is4xxClientError(),
                          response -&amp;gt; response.bodyToMono(String.class)
                                            .map(msg -&amp;gt; new RuntimeException("Hotel not found: " + msg))
                        )
                .onStatus(
                          status -&amp;gt; status.is5xxServerError(),
                          response -&amp;gt; response.bodyToMono(String.class)
                                               .map(msg -&amp;gt; new RuntimeException("Hotel Service error: " + msg))
                        )
                .bodyToMono(HotelResponse.class)
                .block();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Global Exception Handling with @ControllerAdvice&lt;/strong&gt;&lt;br&gt;
At the application level, we can use @RestControllerAdvice to catch exceptions globally and return clean JSON responses instead of stack traces.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(RuntimeException.class)
    public ResponseEntity&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt; handleRuntimeException(RuntimeException ex) {
        Map&amp;lt;String, Object&amp;gt; error = new HashMap&amp;lt;&amp;gt;();
        error.put("error", ex.getMessage());
        error.put("timestamp", LocalDateTime.now());
        error.put("status", HttpStatus.BAD_REQUEST.value());
        return new ResponseEntity&amp;lt;&amp;gt;(error, HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt; handleException(Exception ex) {
        Map&amp;lt;String, Object&amp;gt; error = new HashMap&amp;lt;&amp;gt;();
        error.put("error", "Internal server error");
        error.put("details", ex.getMessage());
        error.put("timestamp", LocalDateTime.now());
        error.put("status", HttpStatus.INTERNAL_SERVER_ERROR.value());
        return new ResponseEntity&amp;lt;&amp;gt;(error, HttpStatus.INTERNAL_SERVER_ERROR);
    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🔹 &lt;strong&gt;Connection Timeouts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Connection timeouts are critical in Spring WebClient to prevent your application from waiting indefinitely when calling slow or unresponsive services. Configuring timeouts ensures microservices remain responsive, resilient, and production-ready.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Types of Timeouts in WebClient&lt;/strong&gt;&lt;br&gt;
With WebClient (backed by Reactor Netty), there are three main timeout types:&lt;br&gt;
&lt;strong&gt;Connection Timeout:&lt;/strong&gt; Maximum time allowed to establish a TCP connection.&lt;br&gt;
&lt;strong&gt;Response Timeout:&lt;/strong&gt; Maximum time to wait for the first byte of the response.&lt;br&gt;
&lt;strong&gt;Read/Write Timeout:&lt;/strong&gt; Maximum time allowed for reading or writing data once connected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example: Configure Timeouts in WebClient&lt;/strong&gt;&lt;br&gt;
Configure timeouts when building a WebClient using Reactor Netty:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Configuration
public class WebClientConfig {
    @Bean
    public WebClient.Builder webClientBuilder() {

        HttpClient httpClient = HttpClient.create()
                // 1. Connection Timeout
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
                // 2. Response Timeout
                .responseTimeout(Duration.ofSeconds(5))
                // 3. Read &amp;amp; Write Timeout
                .doOnConnected(conn -&amp;gt; 
                        conn.addHandlerLast(new ReadTimeoutHandler(5, TimeUnit.SECONDS))
                            .addHandlerLast(new WriteTimeoutHandler(5, TimeUnit.SECONDS))
                );

        return WebClient.builder()
                .clientConnector(new ReactorClientHttpConnector(httpClient));
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Using Request-Level Timeout&lt;/strong&gt;&lt;br&gt;
Add a per-request timeout as an extra safety net:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HotelResponse hotel = webClient
                .get()
                .uri("hotel/{id}", booking.getHotelId())
                .retrieve()
                .bodyToMono(HotelResponse.class)
                .timeout(Duration.ofSeconds(3))
                .block();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🔹&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In microservices, WebClient gives developers a modern and reactive way to handle HTTP communication. Even when used synchronously with .block(), it provides better flexibility and cleaner APIs compared to the legacy RestTemplate. As Spring continues to evolve, WebClient stands out as the go-to option for building robust service-to-service communication.&lt;/p&gt;

</description>
      <category>java</category>
      <category>springboot</category>
      <category>microservices</category>
      <category>webclient</category>
    </item>
    <item>
      <title>Mastering RestTemplate in Spring Boot</title>
      <dc:creator>pallavi kamble</dc:creator>
      <pubDate>Sat, 27 Sep 2025 12:40:50 +0000</pubDate>
      <link>https://dev.to/pallavi_kamble_07/mastering-resttemplate-in-spring-boot-2i84</link>
      <guid>https://dev.to/pallavi_kamble_07/mastering-resttemplate-in-spring-boot-2i84</guid>
      <description>&lt;p&gt;When building microservices or REST-based applications, it’s common to make HTTP calls from one service to another. In Spring Boot, the traditional and widely used approach for this (before WebClient) is RestTemplate. It provides a simple, synchronous way to perform GET, POST, PUT, DELETE, and other HTTP requests. In this post, we’ll explore practical examples of using RestTemplate, including sending custom headers, handling responses, and performing CRUD operations between services like a Customer Service and an Account Service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is RestTemplate?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;RestTemplate is a synchronous client provided by the Spring Framework to make HTTP requests to RESTful web services.&lt;br&gt;
It handles HTTP communication and internally relies on java.net.HttpURLConnection, simplifying interaction with external services.&lt;br&gt;
It offers convenience, flexibility, and seamless integration for various HTTP operations. Think of it as a Java client that enables your Spring Boot application to consume REST APIs easily.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Features of RestTemplate&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Synchronous HTTP Calls&lt;/strong&gt;&lt;br&gt;
RestTemplate executes HTTP requests synchronously, meaning the thread waits for the response before continuing. It’s simple and widely used for service-to-service communication.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Multiple HTTP Methods&lt;/strong&gt;&lt;br&gt;
Supports GET, POST, PUT, PATCH, DELETE, and OPTIONS, making it easy to perform full CRUD operations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Flexible Response Handling&lt;/strong&gt;&lt;br&gt;
Fetch just the response body (getForObject()), access the full response with status and headers (getForEntity()), or use exchange() for complete control over requests and responses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. URI Template Support&lt;/strong&gt;&lt;br&gt;
Use placeholders ({id}) in URLs to dynamically insert values.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Request Body Handling&lt;/strong&gt;&lt;br&gt;
Send Java objects as JSON or XML and automatically map responses to Java objects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Custom Headers&lt;/strong&gt;&lt;br&gt;
Add headers using HttpEntity and HttpHeaders for authentication, tracing, or extra metadata.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Exception Handling&lt;/strong&gt;&lt;br&gt;
RestTemplate throws exceptions for HTTP errors (4xx, 5xx) which can be caught or customized with ResponseErrorHandler.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. Interceptors and Request Factories&lt;/strong&gt;&lt;br&gt;
Use ClientHttpRequestInterceptor for logging or authentication. Swap the underlying HTTP client via ClientHttpRequestFactory for timeouts, pooling, or advanced configuration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setting Up RestTemplate in Spring Boot&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add Dependency&lt;/strong&gt;&lt;br&gt;
If you’re using Spring Boot Starter Web, RestTemplate is already included. Otherwise, add this to your pom.xml:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-boot-starter-web&amp;lt;/artifactId&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Define RestTemplate as a Bean&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Inject RestTemplate&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Service
public class AccountService {

    private RestTemplate restTemplate;

    public AccountService(RestTemplate restTemplate) {  
        this.restTemplate = restTemplate;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Common Methods of RestTemplate&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. GET Operations :&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- getForObject() :&lt;/strong&gt; Fetches a resource as an object.&lt;/p&gt;

&lt;p&gt;Service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    public CustomerDto getAccountWithCustomer(Long customerId) {

        String CUSTOMER_SERVICE_URL = "http://localhost:8081/customers";
        return restTemplate.getForObject(CUSTOMER_SERVICE_URL + "/{id}", 
                                            CustomerDto.class, 
                                            customerId);

    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@GetMapping("/{id}")
    public CustomerDto getAccountWithCustomer(@PathVariable Long id) {
        return accountService.getAccountWithCustomer(id);   
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;cURL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl --location 'http://localhost:8082/accounts/5' \
--data ''
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;- getForEntity() :&lt;/strong&gt; Fetches a resource with HTTP status and headers.&lt;/p&gt;

&lt;p&gt;Service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public CustomerDto getCustomerWithGetForEntity(Long customerId) {
        String CUSTOMER_SERVICE_URL = "http://localhost:8081/customers";
        ResponseEntity&amp;lt;CustomerDto&amp;gt; response = 
                restTemplate.getForEntity(
                                CUSTOMER_SERVICE_URL + "/{id}", 
                                CustomerDto.class, 
                                customerId
                );
        System.out.println("Status Code: " + response.getStatusCode());
        System.out.println("Headers: " + response.getHeaders());
        return response.getBody();
    }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@GetMapping("/cust/{id}")
    public CustomerDto getCustomerWithGetForEntity(@PathVariable Long id) {
        return accountService.getCustomerWithGetForEntity(id);  
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;cURL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl --location 'http://localhost:8082/accounts/cust/5'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;- exchange():&lt;/strong&gt; Flexible method for any HTTP method with headers, body, and status.&lt;/p&gt;

&lt;p&gt;Service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    public CustomerDto getCustomerWithExchange(Long customerId) {
        HttpHeaders headers = new HttpHeaders();
        headers.set("Custom-Header", "customer");
        HttpEntity&amp;lt;Void&amp;gt; entity = new HttpEntity&amp;lt;&amp;gt;(headers);

        ResponseEntity&amp;lt;CustomerDto&amp;gt; response = restTemplate.exchange(
                CUSTOMER_SERVICE_URL + "/{id}",
                HttpMethod.GET,
                entity,
                CustomerDto.class,
                customerId
        );

        System.out.println("Status Code: " + response.getStatusCode());
        System.out.println("Headers: " + response.getHeaders());
        return response.getBody();
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@GetMapping("/exchange/{id}")
    public CustomerDto getCustomerWithExchange(@PathVariable Long id) {
        return accountService.getCustomerWithExchange(id);
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;cURL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl --location 'http://localhost:8082/accounts/exchange/2' \
--header 'Custom-Header: customer' \
--data ''
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. POST Operations:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- postForObject() :&lt;/strong&gt; Sends a POST request and retrieves the response body.&lt;/p&gt;

&lt;p&gt;Service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public CustomerDto createCustomerWithPostForObject(CustomerDto customer) {
        String CUSTOMER_SERVICE_URL = "http://localhost:8081/customers/create";
        return restTemplate.postForObject(CUSTOMER_SERVICE_URL, 
                                            customer, 
                                            CustomerDto.class);
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@PostMapping("/cust")
    public CustomerDto createCustomerWithPostForObject(@RequestBody CustomerDto customer) {
        return accountService.createCustomerWithPostForObject(customer);
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;cURL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl --location 'http://localhost:8082/accounts/cust' \
--header 'Content-Type: application/json' \
--data-raw '{
           "firstName": "Lara",
           "lastName": "Jean",
           "email": "lara@example.com",
           "mobileNo": "09873455672"
         }'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;- postForEntity() :&lt;/strong&gt; Sends a POST request and returns a ResponseEntity containing status, headers, and body.&lt;/p&gt;

&lt;p&gt;Service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public CustomerDto createCustomerWithPostForEntity(CustomerDto customer) {
        String CUSTOMER_SERVICE_URL = "http://localhost:8081/customers/create";
        ResponseEntity&amp;lt;CustomerDto&amp;gt; response = 
                restTemplate.postForEntity(
                CUSTOMER_SERVICE_URL, customer, CustomerDto.class
                );
        System.out.println("Status Code: " + response.getStatusCode());
        return response.getBody();
    } 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@PostMapping("/createCustomer")
    public CustomerDto createCustomerWithPostForEntity(@RequestBody CustomerDto customer) {
        return accountService.createCustomerWithPostForEntity(customer);
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;cURL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl --location 'http://localhost:8082/accounts/customer' \
--header 'Content-Type: application/json' \
--data-raw '        {
           "firstName": "Martin",
           "lastName": "Wood",
           "email": "wood@example.com",
           "mobileNo": "9876655443"
         }'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. PUT Operation:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- put() :&lt;/strong&gt; Updates an existing resource.&lt;/p&gt;

&lt;p&gt;Service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; public String updateCustomer(CustomerDto updatedCustomer) {
        String CUSTOMER_SERVICE_URL = "http://localhost:8081/customers/update";
        restTemplate.put(CUSTOMER_SERVICE_URL, 
                        updatedCustomer);
        return "Customer Updated Successfully...!";
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@PutMapping("/customer")
    public void updateCustomer(@RequestBody CustomerDto customer) {
        accountService.updateCustomer(customer);
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;cURL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl --location --request PUT 'http://localhost:8082/accounts/customer' \
--header 'Content-Type: application/json' \
--data-raw '    {
    "customerId": "2",
  "firstName": "Sam",
  "lastName": "Smith",
  "email": "sam@gmail.com",
  "mobileNo": "9876556651"
}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. DELETE Operation:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- delete() :&lt;/strong&gt; Deletes a resource.&lt;/p&gt;

&lt;p&gt;Service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; public String deleteCustomer(Long customerId) {
        String CUSTOMER_SERVICE_URL = "http://localhost:8081/customers/delete";
        restTemplate.delete(CUSTOMER_SERVICE_URL + "/{id}", 
                            customerId);
        return "Customer Deleted Successfully...!";
    } 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; @DeleteMapping("/customer/{id}")
    public String deleteCustomer(@PathVariable Long id) {
        return accountService.deleteCustomer(id);
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;cURL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl --location --request DELETE 'http://localhost:8082/accounts/customer/11' \
--data ''
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Exception Handling with RestTemplate&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When calling external REST APIs using RestTemplate, things don’t always go as expected. Network issues, unavailable services, or server errors can occur, resulting in exceptions.&lt;br&gt;
Proper exception handling ensures your application can gracefully handle errors, provide meaningful messages, and prevent crashes.&lt;br&gt;
In Spring Boot, you can handle exceptions locally within service methods or globally using @ControllerAdvice and @ExceptionHandler.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common Exceptions&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;HttpClientErrorException : 4xx HTTP errors (e.g., 404 Not Found, 400 Bad Request)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;HttpServerErrorException : 5xx HTTP errors (e.g., 500 Internal Server Error)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ResourceAccessException : Network errors, timeouts, or unreachable host&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;RestClientException : Generic error if none of the above match&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Handle Exceptions Locally&lt;/strong&gt;&lt;br&gt;
Wrap your RestTemplate calls in a try-catch block:&lt;br&gt;
This approach is simple for service-level error handling.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; public CustomerDto getCustomerById(Long customerId) {
        String CUSTOMER_SERVICE_URL = "http://localhost:8081/customers/{id}";
        try {
            return restTemplate.getForObject(CUSTOMER_SERVICE_URL, CustomerDto.class, customerId);
        } catch (HttpClientErrorException.NotFound e) {
            throw new RuntimeException("Customer not found with ID: " + customerId);
        } catch (HttpClientErrorException e) {
            throw new RuntimeException("Client Error: " + e.getStatusCode());
        } catch (HttpServerErrorException e) {
            throw new RuntimeException("Server Error: " + e.getStatusCode());
        } catch (ResourceAccessException e) {
            throw new RuntimeException("Service unreachable: " + e.getMessage());
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Global Exception Handling with @ControllerAdvice&lt;/strong&gt;&lt;br&gt;
You can also handle exceptions globally in your Spring Boot application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(HttpClientErrorException.NotFound.class)
    public ResponseEntity&amp;lt;String&amp;gt; handleNotFound(HttpClientErrorException.NotFound ex) {
        return new ResponseEntity&amp;lt;&amp;gt;("Resource not found: " + ex.getMessage(), 
                                    HttpStatus.NOT_FOUND);
    }

    @ExceptionHandler(HttpClientErrorException.class)
    public ResponseEntity&amp;lt;String&amp;gt; handleClientError(HttpClientErrorException ex) {
        return new ResponseEntity&amp;lt;&amp;gt;("Client Error: " + ex.getStatusCode(), 
                                    HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(HttpServerErrorException.class)
    public ResponseEntity&amp;lt;String&amp;gt; handleServerError(HttpServerErrorException ex) {
        return new ResponseEntity&amp;lt;&amp;gt;("Server Error: " + ex.getStatusCode(), 
                                    HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @ExceptionHandler(ResourceAccessException.class)
    public ResponseEntity&amp;lt;String&amp;gt; handleResourceAccess(ResourceAccessException ex) {
        return new ResponseEntity&amp;lt;&amp;gt;("Service unreachable: " + ex.getMessage(), 
                                    HttpStatus.SERVICE_UNAVAILABLE);
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity&amp;lt;String&amp;gt; handleGenericException(Exception ex) {
        return new ResponseEntity&amp;lt;&amp;gt;("Error: " + ex.getMessage(), 
                                    HttpStatus.INTERNAL_SERVER_ERROR);
    }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How It Works :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When AccountService calls CustomerService via RestTemplate, it might throw exceptions like HttpClientErrorException or ResourceAccessException.&lt;/li&gt;
&lt;li&gt;Spring automatically detects these exceptions in the same application context (AccountService) and routes them to your GlobalExceptionHandler.&lt;/li&gt;
&lt;li&gt;You don’t need to write try-catch in every method, unless you want customized local handling.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Connection Pooling with RestTemplate&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By default, RestTemplate creates a new HTTP connection for each request. This is fine for low-traffic apps, but under load, it can impact the performance. Using connection pooling allows to reuse connections and improve efficiency.&lt;/p&gt;

&lt;p&gt;RestTemplate uses SimpleClientHttpRequestFactory, which:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Opens a new TCP connection for every HTTP request.&lt;/li&gt;
&lt;li&gt;Closes the connection immediately after the request is done.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every new connection involves TCP handshake + SSL handshake (if HTTPS) → expensive.&lt;/li&gt;
&lt;li&gt;For high-traffic microservices or APIs, this leads to performance bottlenecks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Connection pooling.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep a pool of reusable HTTP connections open.&lt;/li&gt;
&lt;li&gt;Requests reuse idle connections instead of creating new ones.&lt;/li&gt;
&lt;li&gt;Reduces latency and improves throughput.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How RestTemplate Uses Connection Pooling&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;RestTemplate doesn’t support pooling directly. We need Apache HttpClient.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Apache HttpClient has PoolingHttpClientConnectionManager that manages reusable connections.&lt;/li&gt;
&lt;li&gt;HttpComponentsClientHttpRequestFactory allows RestTemplate to use this HttpClient.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Add Apache HttpClient Dependency&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.apache.httpcomponents.client5&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;httpclient5&amp;lt;/artifactId&amp;gt;
        &amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Configure RestTemplate with Connection Pooling&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate() {

        // 1. Setup connection pool
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
        connectionManager.setMaxTotal(50);      
        connectionManager.setDefaultMaxPerRoute(20);

        // 2. Build HttpClient with connection manager
        CloseableHttpClient httpClient = HttpClients.custom()
                .setConnectionManager(connectionManager)
                .build();

        // 3. Create RestTemplate factory
        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
        factory.setHttpClient(httpClient);
        factory.setConnectTimeout(5000);
        factory.setReadTimeout(5000);

        return new RestTemplate(factory);

    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;MaxTotal: Maximum connections the pool can hold in total.&lt;/li&gt;
&lt;li&gt;DefaultMaxPerRoute: Maximum connections to a single host.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example: If your app talks to 3 services, the pool can hold up to 50 connections overall, but a single service will not exceed 20 concurrent connections.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CloseableHttpClient is a thread-safe HttpClient. It uses the PoolingHttpClientConnectionManager internally.&lt;/li&gt;
&lt;li&gt;Connect Timeout: Max time to establish TCP connection.&lt;/li&gt;
&lt;li&gt;Read Timeout: Max time to wait for the response after connection.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ensures that your app doesn’t hang indefinitely if a service is slow or unresponsive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Benefits of Using Connection Pooling&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reduced latency : Avoids repeated TCP/SSL handshakes.&lt;/li&gt;
&lt;li&gt;Improved throughput : Handles more requests simultaneously.&lt;/li&gt;
&lt;li&gt;Resource efficiency : Less CPU and network usage.&lt;/li&gt;
&lt;li&gt;Stability under load : Prevents connection exhaustion during spikes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note on Deprecation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;RestTemplate has been officially deprecated by the Spring team and is no longer being actively developed. The recommended alternative is WebClient, which offers non-blocking, reactive, and more scalable HTTP communication.&lt;/p&gt;

&lt;p&gt;That said, RestTemplate is still heavily used in real-world projects and widely found in production codebases. Understanding it is essential if you’re maintaining existing applications or working in environments that haven’t fully migrated to WebClient.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We explored RestTemplate in depth — from its features and flexible methods to practical CRUD examples, handling custom headers, and managing exceptions. RestTemplate has long been the go-to tool for synchronous HTTP communication in Spring Boot, making it easy to connect services in a microservice architecture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Takeaway:&lt;/strong&gt; RestTemplate may be deprecated, but with proper setup — including exception handling and connection pooling — it remains a powerful tool in legacy systems, while WebClient leads the way for modern applications.&lt;/p&gt;

</description>
      <category>java</category>
      <category>springboot</category>
      <category>microservices</category>
      <category>restapi</category>
    </item>
  </channel>
</rss>
