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 is a library provided by the Spring framework to invoke web services to a client.
Feign is a declarative web service client written in Java. It simplifies the process of calling RESTful web services by handling the details of communication and encoding/decoding request/response payloads. FeignClient is an interface that is automatically implemented by the Feign framework at runtime to provide a client with a specific service. It allows you to define the methods for calling different endpoints of a service, and it uses HTTP requests under the hood to interact with the service.
It also has pluggable annotation support including Feign annotations and JAX-RS annotations.
JAX-RS is Java API that helps to write web services. Developers mark the java class and their methods with JAX-RS annotations to define resources and the actions to be performed on those resources.

Some of the important JAX-RS annotations are 


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

The main advantage of the Spring Cloud OpenFeign is that we don’t need to write any code for calling a web service. We just need to create an interface and annotate it with Feign annotation. The methods declared in this interface are annotated with JAX-RS annotations to call the web service.

We will see how to use Feign annotations in the next stages by building a simple application.

The difference between Spring Boot FeignClient and WebClient

FeignClient and WebClient are both Java libraries for making HTTP requests to remote servers. However, they are designed for different use cases and have some key differences:

FeignClient is a library for creating declarative REST clients. It simplifies the process of writing Java code for making HTTP requests by automatically generating a lot of the boilerplate code for you. FeignClient uses annotations to define the API endpoints, and it can automatically marshal and unmarshal data to and from JSON. FeignClient also allows you to easily switch between different HTTP transport libraries, like OkHttp or Apache HTTP Client. It is built on top of the Retrofit library.

WebClient, on the other hand, is a non-blocking, reactive client for making HTTP requests. It is part of the Spring WebFlux framework, and it is designed to work with the reactive programming model. WebClient can handle high concurrency and is well-suited for streaming large amounts of data. Unlike FeignClient, WebClient does not have a declarative API, and you need to manually build the HTTP requests and handle the responses.

In summary, FeignClient is a good choice if you're working with Spring Cloud and want a simple, declarative API for making HTTP requests, while WebClient is more suitable for high-concurrency and reactive use cases.

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

Now we will create a simple microservice for processing payment for the orders that passed from the order-processing-service i.e. a different microservice

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 is a fairly simple payment service that only demonstrates the working of OpenFeign. The actual payment service will be much more complicated than this.

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: Kindly add the required dependencies for Spring Data JPA and MySQL connector in your pom.xml as we will be using MySQL database for storing our orders table



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;
    
}

I have used lombok annotations in all the above classes that eliminates the boilerplate code for default constructor, parameterized constructor, 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.