In a microservices architecture, smooth and effective inter-service communication is essential to construct scalable and maintainable applications. Spring Cloud OpenFeign allows for smooth communication between services through the declarative HTTP client, with little boilerplate code required. With the integration of Spring Cloud Eureka for service discovery and load balancing features, the flexibility and reliability of microservices are increased with OpenFeign. This article explores how OpenFeign can improve microservice communication among Spring Boot applications, thereby profiting from efficiency, scalability, and maintainability.
Spring Cloud OpenFeign is an amazing tool for creating HTTP clients in any Spring-based microservice. OpenFeign allows you to call other services without the overhead of configuring the HTTP requests, error handling, or response processing.
Steps to Set Up OpenFeign in Your Spring Boot Project:#
2. Create a Client Interface (Add @FeignClient) inside order-service#
@FeignClient(name = "service name") → Defines a Feign client for inter-service calls.
@FeignClient(name = "service name", path = "base path") → Defines a Feign client for order-service with a base path.
Advantages:
Avoids duplicate URL definitions
Keeps Feign clients clean and organized
Works with Eureka for dynamic service discovery
package com.codingshuttle.ecommerce.order_service.clients;import com.codingshuttle.ecommerce.order_service.dtos.OrderRequestDto;import org.springframework.cloud.openfeign.FeignClient;import org.springframework.web.bind.annotation.PutMapping;import org.springframework.web.bind.annotation.RequestBody;@FeignClient(name="inventory-service",path = "/inventory") //name = application name, path = base pathpublic interface InventoryOpenFeignClient { @PutMapping("/products/reduce-stocks") //use same path and mapping Double reduceStocks(@RequestBody OrderRequestDto orderRequestDto); //use same method as order-service// The OrderRequestDto class is already created inside the previous article.}
3. Add @EnableFeignClients inside the main application class (order-service)#
package com.codingshuttle.ecommerce.order_service;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.openfeign.EnableFeignClients;@SpringBootApplication@EnableFeignClients // for bean creationpublic class OrderServiceApplication { public static void main(String[] args) { SpringApplication.run(OrderServiceApplication.class, args); }}
4. Create a post mapping to create a order in OrderController (order-service)#
package com.codingshuttle.ecommerce.order_service.controllers;@RestController@RequiredArgsConstructor@RequestMapping("/core")@Slf4jpublic class OrderController { private final OrderService orderService;@PostMapping("/create-order") public ResponseEntity<OrderRequestDto> createOrder(@RequestBody OrderRequestDto orderRequestDto){ //The OrderRequestDto class is already created inside the previous article. OrderRequestDto orderRequestDto1 = orderService.createOrder(orderRequestDto); return ResponseEntity.ok(orderRequestDto1); } }
5. Create a business logic for post mapping to create an order in OrderService (order-service)#
package com.codingshuttle.ecommerce.order_service.services;@Service@Slf4j@RequiredArgsConstructorpublic class OrderService { private final OrderRepo orderRepo; //OrderRepo is already created inside the previous article. private final ModelMapper modelMapper; //Modelmapper is already created inside the previous article. private final InventoryOpenFeignClient inventoryOpenFeignClient; public OrderRequestDto createOrder(OrderRequestDto orderRequestDto) { log.info("Calling the createOrder method"); Double totalPrice = inventoryOpenFeignClient.reduceStocks(orderRequestDto); //OrderEntity is already created inside the previous article. OrdersEntity orders = modelMapper.map(orderRequestDto,OrdersEntity.class); for(OrderItemsEntity orderItem: orders.getItems()){ //OrderItemsEntity is already created inside the previous article. orderItem.setOrder(orders); } orders.setTotalPrice(totalPrice); orders.setOrderStatus(OrderStatus.CONFIRMED); //OrderStatus is already created inside the previous article. OrdersEntity savedOrder = orderRepo.save(orders); return modelMapper.map(savedOrder,OrderRequestDto.class); } }
6. Create OrderRequestDto And OrderRequestItemDto Class (inventory-service)#
package com.codingshuttle.ecommerce.inventory_service.dtos;import lombok.Data;import java.util.List;@Datapublic class OrderRequestDto { private List<OrderRequestItemDto> items;}package com.codingshuttle.ecommerce.inventory_service.dtos;import lombok.Data;@Datapublic class OrderRequestItemDto { private Long productId; private Integer quantity;}
7. Create a put mapping for reduce-stock in ProductController (inventory-service)#
package com.codingshuttle.ecommerce.inventory_service.controllers;@RestController@Slf4j@RequiredArgsConstructor@RequestMapping("/products")public class ProductsController { private final ProductService productService; private final OrderFeignClient orderFeignClient; @PutMapping("reduce-stocks") public ResponseEntity<Double> reduceStocks(@RequestBody OrderRequestDto orderRequestDto){ Double totalPrice = productService.reduceStocks(orderRequestDto); return ResponseEntity.ok(totalPrice); } }
8. Create a business logic for put mapping for reduce-stock in ProductService (inventory-service)#
package com.codingshuttle.ecommerce.inventory_service.services;@Service@Slf4j@RequiredArgsConstructorpublic class ProductService { private final ProductRepo productRepo; @Transactional public Double reduceStocks(OrderRequestDto orderRequestDto) { log.info("Reducing the stocks"); Double totalPrice = 0.0; for(OrderRequestItemDto orderRequestItemDto: orderRequestDto.getItems()){ Long productId = orderRequestItemDto.getProductId(); Integer quantity = orderRequestItemDto.getQuantity(); ProductEntity product = productRepo.findById(productId) .orElseThrow(()-> new RuntimeException("Product not found with id: "+productId)); if(product.getStock() < quantity){ throw new RuntimeException("Product cannot be fulfilled for given quantity"); } product.setStock(product.getStock() - quantity); productRepo.save(product); totalPrice += quantity*product.getPrice(); } return totalPrice; }}
After running all services (the sequence is 1. Run Eureka Server (Discovery Service) 2. Run other clients services. 3. Last run gateway service on default port 8080)
2. Wait for all registered client services inside Eureka:#
if your services are not fully registered before another service tries to communicate with them, you may encounter a "Service Unavailable" exception.
After starting Eureka and all client services, test the API using a browser (for GET requests) or Postman (for all requests). If you are trying to fetch other services you can easily fetch it. Here, we are trying to fetch inventory service data by using api-gateway (port=8080). /api/v1 ignored because of using StripPrefix=2 . We need to comment “RedirectTo=302, <http://codingshuttle.com>” and “AddRequestHeader=X-Custom-Header, ABCD” from the gateway filter.
Output of postman
Ensure the service is registered, then hit the correct URL. If you get a "503 Service Unavailable," wait and retry.
This article expounds on Spring Cloud OpenFeign, a declarative HTTP client for Spring Boot microservices, which is meant to make communication through REST APIs less boilerplate. It includes servicediscovery integration, load balancing, and custom configuration, as well as giving a new meaning to microservice efficiency, scalability, and maintainability.
Want to Master Spring Boot and Land Your Dream Job?
Struggling with coding interviews? Learn Data Structures & Algorithms (DSA) with our expert-led course. Build strong problem-solving skills, write optimized code, and crack top tech interviews with ease