Spring Cloud OpenFeign — REST client for Spring Boot App

Spring Cloud OpenFeign provides OpenFeign integrations for Spring Boot apps through autoconfiguration and binding to the Spring Environment and other Spring programming model idioms.

GraphQL has a role beyond API Query Language- being the backbone of application Integration
background Coditation

Spring Cloud OpenFeign — REST client for Spring Boot App

Overview

Spring Cloud OpenFeign simplifies remote service invocation, enabling seamless communication between microservices. It's a powerful tool within the Spring ecosystem for building robust and scalable distributed systems.
Feign, a powerful Java library, simplifies the complex task of interacting with RESTful web services. By handling the intricacies of HTTP requests, encoding, and decoding, Feign empowers developers to focus on defining service interactions. Through its intuitive interface, FeignClient, developers can effortlessly create methods that map directly to specific service endpoints. This elegant approach abstracts away the underlying HTTP mechanisms, enabling seamless communication with remote services.
Highly customizable with pluggable annotation support, including Feign and JAX-RS annotations.

JAX-RS: A powerful Java framework for crafting RESTful web services. By annotating Java classes and methods, developers can effortlessly define and implement web resources and their associated operations.

Some of the important JAX-RS annotations are 


@Path
@Get
@Post
@Put 
@Delete
@QueryParam
@PathParam

Spring Cloud OpenFeign streamlines microservices communication by eliminating the need for manual HTTP client code. Developers simply define interfaces annotated with Feign and JAX-RS annotations, and OpenFeign handles the rest, making microservices integration efficient and hassle-free.

Subsequent sections will delve into practical Feign annotation usage through a straightforward application.

The difference between Spring Boot FeignClient and WebClient

When it comes to crafting robust HTTP requests in Java, FeignClient and WebClient are two popular choices. While both libraries excel at facilitating communication with remote servers, they cater to distinct use cases and possess unique strengths.

Simplify your Java REST client development with FeignClient. This powerful library automates much of the boilerplate code, allowing you to focus on defining API endpoints using annotations. It seamlessly handles JSON serialization and deserialization while offering flexibility in choosing HTTP transport libraries like OkHttp or Apache HTTP Client. Built on the robust foundation of Retrofit, FeignClient empowers you to create efficient and maintainable RESTful clients.

Unlike FeignClient's declarative style, WebClient offers a non-blocking, reactive approach to HTTP requests. As part of the Spring WebFlux framework, it aligns seamlessly with reactive programming principles. This makes it ideal for handling high concurrency and efficient streaming of large datasets. Developers must construct HTTP requests and process responses manually, providing granular control over network interactions.

For Spring Cloud applications requiring straightforward, declarative HTTP client capabilities, FeignClient is an excellent choice. However, when high-concurrency and reactive programming are paramount, WebClient offers a superior solution.

Dependencies Required

While creating a spring boot project, the first step is to add the spring cloud version under the properties tag in the pom.xml file


<properties>
	<java.version>17</java.version> 
		<spring-cloud.version>2022.0.0</spring-cloud.version>
</properties>

Next, we have to add openfeign and spring cloud dependencies


<dependency>
		<groupId>org.springframework.cloud</groupId> 
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>


<dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
      	<artifactId>spring-cloud-dependencies</artifactId>
        <version>${spring-cloud.version}</version>
        <type>pom</type>
        <scope>import</scope>
		<dependency> 
	</dependencies> 
</dependencyManagement>


Communication Between Microservices Using Spring Cloud OpenFeign

Let's build a simple example to demonstrate how OpenFeign works.
In this example, we will build 2 simple microservices for an e-commerce application - order-processing-service and payment-service.

Creating payment-service

Next, we'll develop a streamlined microservice dedicated to handling payment processing for orders successfully routed from our order-processing service.

Step 1: Create PaymentResponseVO class for returning the response object


@Builder
@Data
public class PaymentResponseVO {
private String orderId;
private String paymentStatus;
private String paymentReferenceNumber;
}

Step 2: Create an interface for the constants


package com.example.paymentservice. Constants;

public interface PaymentsConstants {

2 usages
String PAYMENT_SUCCESS = "PAYMENT_SUCCESS";
1 usage
String PAYMENT_FAILURE = "PAYMENT_FAILURE";
}


Step 3: Create the controller class



@RestController
public class PaymentServiceController {

@GetMapping (©~"/v1/payments/status")
public ResponseEntity<PaymentResponseVO> getPaymentStatus (@RequestParam String orderId,
																													 @RequestParam double orderAmount) {
return ResponseEntity.ok(PaymentResponseVo.builder()
			.orderAmount (orderAmount)
			.orderId (orderId)
			.paymentStatus (PAYMENT_SUCCESS)
			.paymentReferenceNumber(UUID.randomUUID().toString())
			.buildO);
	}
}


NOTE: This straightforward payment service showcases the foundational capabilities of OpenFeign. Real-world payment systems often incorporate significantly more complex functionalities.

Creating order-processing-service

Now we will create the order processing service that will call the payment service using OpenFeign

Step 1: Create an interface with @FeignClient annotation. The @FeignClient annotation should have properties name and url

@FeignClient(name = “payment-client”, url = “${payment-service-url})”


package com.ekart.orderprocessingservice. feign;

import com.ekart.orderprocessingservice.model. PaymentResponseVO;
import org.springframework.cloud. openfeign. FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind. annotation. RequestParam;

3 usages
@FeignClient (name = "payment-client", url = "${pavemnt-service-urlf")
public interface PaymentClient {

	1 usage
	@GetMapping(©v"/v1/payments/status")
	public PaymentResponseVO getPaymentStatus (@RequestParam String orderId, @RequestParam double orderAmount);
}

Here getPaymentStatus method will fetch the payment status from the PaymentService using the API that has been exposed by the service in the Create payment-service section. 

Step 2: Add @EnableFeignClients to our main class. @EnableFeignClients annotation enables component scanning for interfaces that declare that they are Feign clients.


@SpringBootApplication
@EnableFeignClients
public class OrderProcessingServiceApplication {

		public static void main(String[] args) {
				SpringApplication.run (OrderProcessingServiceApplication.class, args);
			}

}

Step 3: Create OrderProcessingService.java class that will contain our business logic


package com.ekart.orderprocessingservice.service;

import com.ekart.orderprocessingservice.entity.Order;
import com.ekart.orderprocessingservice.feign.PaymentClient;
import com.ekart.orderprocessingservice.model.OrderResponseVO;
import com.ekart.orderprocessingservice.model.OrderVO;
import com.ekart.orderprocessingservice.model.PaymentResponseVO;
import com.ekart.orderprocessingservice.repository.OrderRepository;
import org.springframework.stereotype.Service;

import static com.ekart.orderprocessingservice.Constants.OrderConstants.*;

@Service
public class OrderProcessingService {

   private OrderRepository orderRepository;
   private PaymentClient paymentClient;

   public OrderProcessingService(OrderRepository orderRepository, PaymentClient paymentClient) {
       this.orderRepository = orderRepository;
       this.paymentClient = paymentClient;
   }

   public OrderResponseVO createOrder(OrderVO order) {
       String orderId = order.getOrderId();
       double orderAmount = order.getOrderAmount();
       //Calling payment service
       PaymentResponseVO paymentResponse = paymentClient.getPaymentStatus(orderId, orderAmount);
       if(PAYMENT_SUCCESS.equals(paymentResponse.getPaymentStatus())) {
           orderRepository.save(Order.builder()
                   .orderId(orderId)
                   .orderAmount(orderAmount)
                   .orderStatus(ORDER_SUCCESS)
            .paymentReferenceNumber(paymentResponse.getPaymentReferenceNumber()).build());
      } else {
           orderRepository.save(Order.builder()
                   .orderId(orderId)
                   .orderAmount(orderAmount)
                   .orderStatus(ORDER_FAILURE)
                   .paymentReferenceNumber("N/A")
                   .build());
       }

       Order savedOrder = orderRepository.findOrderByOrderId(orderId);

       return OrderResponseVO
               .builder()
               .orderId(orderId)
               .orderAmount(savedOrder.getOrderAmount())
               .orderStatus(savedOrder.getOrderStatus())
               .paymentReferenceNumber(savedOrder.getPaymentReferenceNumber())
               .build();
   }
}

Here orderRepository and paymentClient bean have been injected using the constructor injection.

The OrderRepository is an interface that extends the JpaRepository interface for easy access to the MySQL database.

We have added the essential classes, now we need to complete the OrderProcessingService by creating all the remaining classes and interfaces.

Step 3: create all the other remaining classes and interfaces

OrderRepository interface

NOTE: To integrate Spring Data JPA with a MySQL database for our order management system, please include the necessary dependencies in your pom.xml file.



public interface OrderRepository extends JpaRepository<Order, Integer> {

1 usage
Order findOrderByOrderId(String orderId);
}

OrderConstants interface



package com.ekart.orderprocessingservice.Constants;
public interface OrderConstants {

		1 usage
String ORDER_SUCCESS = "ORDER_SUCCESS";
		1 usage
String ORDER_FAILURE = "ORDER_CREATION_FAILURE";
		1 usage
String PAYMENT_SUCCESS = "PAYMENT_SUCCESS";
}

Order class which acts as an entity


@Entity
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table (name = "tbl orders")
public class Order {

@Id
@GeneratedValue (strategy = GenerationType .AUTO)
@Column (name = "id")
private int id;

@Column (name = "order id")
private String orderId;

@Column (name = "order status")
private String orderStatus;

@Column (name = "order amount")
private double orderAmount;

@Column (name = "payment reference number")
private String paymentReferenceNumber;


All the model classes i.e. OrderResponseVO, PaymentRepsonseVO and OrderVO


/**
* This class represents response attributes
*/
5 usages
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class OrderResponseVO {

		private String orderId;
		private String orderStatus;
		private double orderAmount;
		private String paymentReferenceNumber;
}



@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class PaymentResponseVo {

		private String orderId;
		private String paymentStatus;
		private double orderAmount;
		private String paymentReferenceNumber;
}



/**
* This class represents the request attributes
*/

4 usages
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class OrderVO {
		private String orderId;
		private double orderAmount;
    
}

Leveraged Lombok annotations to streamline class definitions, automating the generation of standard boilerplate code (default and parameterized constructors, getters, and setters).

Last but not least let uscreate the controller class that will be the starting point of our application


@RestController
public class OrderProcessingController {

		2 usages
		private OrderProcessingService orderProcessingService;

		public OrderProcessingController (OrderProcessingService orderProcessingService) {
			this.orderProcessingService = orderProcessingService;
		}
		
    @PostMapping (Ov"/v1/order")
		public ResponseEntity<OrderResponseVO> create0rder (@RequestBody OrderVO order) {
				return ResponseEntity.ok(orderProcessingService.create0rder(order));
	}
}


Testing the application

Now, the most exciting part let's test our mini application.

Run both microservices at different ports. You can set the port in application.properties file

Hit the API exposed by order-processing-service using postman

Mini application testing  Spring cloud OpenFeign

As you can see we are creating order in our database and we are getting a paymentReferenceNumber if the payment is successful from the payment-service.
Let's check MySQL workbench if the order has been created or not

MySQL workbench - Spring Cloud OpenFeign

The order has been successfully stored in our database.

Hi, I am Ankit Pradhan. I am a Java backend developer specialized in Spring Framework. In love with solving business related problems with the help of code. In my free time, you will either find me watching a thriller movie or reading a technical blog.

Want to receive update about our upcoming podcast?

Thanks for joining our newsletter.
Oops! Something went wrong.

Latest Articles

Implementing Custom Instrumentation for Application Performance Monitoring (APM) Using OpenTelemetry

Application Performance Monitoring (APM) has become crucial for businesses to ensure optimal software performance and user experience. As applications grow more complex and distributed, the need for comprehensive monitoring solutions has never been greater. OpenTelemetry has emerged as a powerful, vendor-neutral framework for instrumenting, generating, collecting, and exporting telemetry data. This article explores how to implement custom instrumentation using OpenTelemetry for effective APM.

Mobile Engineering
time
5
 min read

Implementing Custom Evaluation Metrics in LangChain for Measuring AI Agent Performance

As AI and language models continue to advance at breakneck speed, the need to accurately gauge AI agent performance has never been more critical. LangChain, a go-to framework for building language model applications, comes equipped with its own set of evaluation tools. However, these off-the-shelf solutions often fall short when dealing with the intricacies of specialized AI applications. This article dives into the world of custom evaluation metrics in LangChain, showing you how to craft bespoke measures that truly capture the essence of your AI agent's performance.

AI/ML
time
5
 min read

Enhancing Quality Control with AI: Smarter Defect Detection in Manufacturing

In today's competitive manufacturing landscape, quality control is paramount. Traditional methods often struggle to maintain optimal standards. However, the integration of Artificial Intelligence (AI) is revolutionizing this domain. This article delves into the transformative impact of AI on quality control in manufacturing, highlighting specific use cases and their underlying architectures.

AI/ML
time
5
 min read