There are many libraries available for Java to consume restful web services, and in the spring boot, Java developers mostly favor the Rest Template to consume the APIs. No doubt, the Rest Template is absolutely acceptable to use in an enterprise Java application.
The RestTemplate required us to write more code, and the more lines of code you write, the more bugs may live in your application. What if I tell you you can get rid of most of the boilerplate code of RestTemplate to consume the API?
Yes, you read correctly. We can use the Open Feign client to consume the Rest APIs in the Spring Boot applications.
If you are a Java developer and you are using the Rest Template in your Spring Boot application and you are curious to explore some more libraries to consume the Rest APIs, then you have landed in the correct article.
In this article, I'm going to walk you through how to use an open-feign client to consume the APIs in the Spring Boot application, and I can ensure that you can gain a lot of confidence from this article to use in your next production-ready code.
What is Open Feign Client, and why should you use it in your next project?
Open-Feign-Client is a library to consume the Rest APIs. Open-Feign-Client comes under the umbrella of the Spring Cloud project. Some of the advantages of open feign clients are listed below.
- Open Feign Cleint is widely used in the Java-based Spring Boot microservice architecture. It provides the load balancing feature out of the box by just implementing the service registry.
- The URL and the port can be dynamically fetched from the service registry.
- Only create abstract methods to consume the APIs.
Dependencies are required to use the Open Feign Client
Maven openfeign dependency
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
Maven Spring cloud 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>
You can find the lates version of above dependencies from here
Annotations
There are many annotations available for the open feign client library, but only two are widely used, and they are as follows:
@EnableFeignClients annotation
The @EnableFeignClients
annotation is used at the class level of the main method. The purpose of this annotation is to instruct the spring boot application at the boot level to scan the inferfaces that are declared with the @FeignClient
annotation.
@SpringBootApplication
@EnableFeignClients
public class OpenFeignClientApplication
{
public static void main(String[] args)
{
SpringApplication.run(OpenFeignClientApplication.class, args);
}
}
@FeignClient annotation
The @FeignClient
annotation is an interface level to declare the Rest API client. There are two main parameters used in this @FeignClient annotation, and they are as follows:
value: The name of the service, and if the service registry is enabled in the microservice architecture, then this name will be utilized by the service registry to load balance across all available instances of the service.
(alert-passed)
url: The url parameter expects the URL of the service that you are required to fetch. It should be the base URL only, not an absolute URL, for example https://www.learnjavaskills.in/
(alert-passed)
@FeignClient(value = "student", url="localhost:8080/student/")
public interface StudentProxy
{
}
How to consume the open Rest API using the open feign client
Let's consume one of the open APIs using the open feign client library. I've got one service running locally on my machine, and that is called the student service.
The student service exposes one rest API that shares the student's details. Take a look at the following student controller method for more clarity.
@GetMapping("all-students")
public ResponseEntity<List<Student>> getAllStudents()
{
List<Student> allStudents = studentService.getAllStudents();
return ResponseEntity.ok(allStudents);
}
The expected output of the above API is as follows:
[
{
id: 1,
firstname: "John",
lastname: "pear"
},
{
id: 2,
firstname: "Mayur",
lastname: "Rathod"
},
{
id: 3,
firstname: "Imran",
lastname: "Shaikh"
}
]
The full rest API endpoint looks like this:
http://localhost:5000/api/student/all-students(code-box)
Let's configure the above Rest endpoint in our other service where we are expecting to consume this API
Step 1: Add the base URL of the above student rest API into the application.properties
file like below. The reason for adding this in the properties file is that we will hook up this base url from the application.properties files into the @FeignClient
interface, where we are configuring our proxy to consume the student service.
student.base-url = http://localhost:5000/api/student/(code-box)
Step 2: Now Create one interface with the @FeignClient annotation and annotate the main method class with @EnableFeignClients. So that, Spring Boot looks up the interfaces, which are annotated with the @FeignClient, to configure the proxy.
@FeignClient(value = "student", url="${student.base-url}")
public interface StudentProxy
{
}
As you can see, the url parameter of the @FeignClient is "${student.base-url}"
, It's mean the value of the key, i.e., student.base-url should be fetched from the application.properties file. In this case, the value would be http://localhost:5000/api/student/
which we configured in step 1.
Step 3: Once the above setup is successfully completed, we can look forward to the next step. Let's create an abstract method that will be used to fetch the students details.
@GetMapping("all-students")
public List<Student> getAllStudent();
public record Student(Long id, String firstname, String lastname)
{}
I've use record in this example and If you are using java 8 or 11 then you can also use POJO as well
If you have closely observed that, we declare one abstract method, i.e., getAllStudent()
, with the List <Student> return type, and then we have annotated this abstract method with the @GetMapping().
The intention of this @GetMapping annotation is that we are instructing the feign client that the HTTP method of the above-mentioned proxy is GET
. So behind the scenes, the feign client will make an API call with the above URL using the HTTP GET method.
Unlike the HTTP GET method, Feign Client also supports other HTTP methods such as PUT for @PutMapping
, POST for @PostMapping
, and DELETE for @DeleteMapping
annotations, respectively.
You can also use the @RequestMapping annotation on the abstract just like we use in the Rest Controller. It works perfectly fine. You can give it a try.
It's time to test feign client API calling
Let's create a service class to call the StudentProxy interface the way we usually do.
@org.springframework.stereotype.Service
public class Service
{
private final StudentProxy studentProxy;
@Autowired
public Service(StudentProxy studentProxy)
{
this.studentProxy = studentProxy;
}
public List<Student> getStudents()
{
return studentProxy.getAllStudent();
}
}
Now, we will call the above service method to print the data that we are getting from the Student Service Rest API. I've got to write code in the main method because I don't want to expose any rest API endpoints in that service.
@SpringBootApplication
@EnableFeignClients
public class OpenFeignClientApplication
{
public static void main(String[] args)
{
ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(OpenFeignClientApplication.class, args);
Service service = (Service) configurableApplicationContext.getBean("service");
System.out.println("Fetching all students");
List<Student> allStudent = service.getStudents();
allStudent.stream()
.forEach(System.out :: println);
}
}
The output of the above code is as follows:
Fetching all students
Student[id=1, firstname=John, lastname=pear]
Student[id=2, firstname=Mayur, lastname=Rathod]
Student[id=3, firstname=Imran, lastname=Shaikh](code-box)
ResponseEntity in the feign client interface
If you are curious to know, before getting the response from the body, I want to first check the header of the response or the status code of the response.
The feign client also supports the ResponseEntity
class. With the help of the ResponseEntity class, you can read the header, status, and body of the response. You just need to replace the return type with the ResponseEntity class instead of using the body objects, just like below.
@FeignClient(value = "student", url="${student.base-url}")
public interface StudentProxy
{
@GetMapping("all-students")
ResponseEntity<List<Student>> getAllStudent();
}
Service can be changed as follows:
public List<Student> getStudents()
{
ResponseEntity<List<Student>> response = studentProxy.getAllStudent();
if (response.getStatusCode().value() == 200)
return response.getBody();
return Collections.emptyList();
}
How to consume a secure Rest API using an open feign client?
Till now, we have only seen how to consume the open Rest API. If you are working in an enterprise Java application, then you are most likely consuming the secure API. Let's dig into how we will be able to pass an authorization token in the header of the API request.
Yes, you read correctly. In this section, we will also learn how we are going to pass data in the API request header using the open feign client. However, I'm taking an example of the authorization; you can feel free to send any other request from the header. The process will be exactly the same.
Are you excited to explore how to send authorization tokens? Let's start right below.
What is the @RequestHeader annotation in the feign client?
The annotation request header means that data should be passed to the request header of the Rest API. As a developer, you may need to send some data in the header of the API request. In such a case, you can utilize the @RequestHeader
annotation.
There are two widely used parameters in the @RequestHeader annotation, and they are as follows:
value: The value defines which header parameter should be aligned. In our case, we are going to send the Basic token under the authorization parameter of the request header for authentication and authorization. (alert-passed)
required: The required argument of the @RequestHeader annotation accepts the boolean value. This argument can be utilized to instruct whether the request header parameter is mandatory to consume the API. By default, it's true. (alert-passed)
Update the code to send the authorization token in the request header
In this example, we are going to use the same student API; I've just enabled the spring security in that service to demonstrate this exercise.
Step 1: Update the student proxy to accept the authorization token in the parameter and annotate it with the @RequestHeader, just like below.
@FeignClient(value = "student", url="${student.base-url}")
public interface StudentProxy
{
@GetMapping("all-students")
ResponseEntity<List<Student>> getAllStudent(@RequestHeader(value = "Authorization", required = true) String authorizationToken);
}
Step 2: Modify the service layer to generate the Bassic authorization token and pass it to the student proxy just like below.
public List<Student> getStudents()
{
String username = "learnjavaskills.in";
String password = "password";
String credential = username + ":" + password;
byte[] encoded = Base64.getEncoder().encode(credential.getBytes());
String authorizationToken = "Basic " + new String(encoded);
ResponseEntity<List<Student>> response = studentProxy.getAllStudent(authorizationToken);
if (response.getStatusCode().value() == 200)
return response.getBody();
return Collections.emptyList();
}
In this section, we have seen how we can send the Basic token, but in most APIs, you may be required to send the Bearer token. In that case, the process is exactly the same: you can use @RequestHeader in the proxy, and you just need to change the mechanism of generating the bearer token.
Dynamic URL in the open feign client
There are some scenarios where you need to consume some of the Rest API by using the dynamic URL. Till now, we have consumed an API with a static URL. Are you excited to learn how to send the URL in the open feign client at runtime? If yes, then let's dive into it.
Before working on the runtime URL, first understand when we may encounter such a requirement. One may say that if the API uses HATEOAS (Hypermedia as the Engine of Application State) that case we can get several URLs apart from the data in the Rest API response, and we are now bound to execute those links from the HATEOAS response.
This is one of my favorite parts of this: connecting the API with the runtime URL.
How URIs are used in the open feign client
To consume the rest API by sending the runtime url in the open feign client, we just need to accept the URI parameter in the proxy abstract method, just like below: Again, we are using the same student API, but this time we send the URL from the run time.
@FeignClient(value = "student", url="${student.base-url}")
public interface StudentProxy
{
@GetMapping()
ResponseEntity<List<Student>> getAllStudent(@RequestHeader(value = "Authorization", required = true) String authorizationToken,
URI uri);
}
If you have closely monitored that, this time we are not appending any static URL under the @GetMapping
annotation, and still open feign clients will be able to consume APIs because of those URI parameters.
Now we need to convert the string url into the URI and pass it to the student proxy abstract method.
public List<Student> getStudents() throws URISyntaxException
{
String username = "learnjavaskills.in";
String password = "password";
String credential = username + ":" + password;
byte[] encoded = Base64.getEncoder().encode(credential.getBytes());
String authorizationToken = "Basic " + new String(encoded);
String url = "http://localhost:5000/api/student/all-students";
URI uri = new URI(url);
ResponseEntity<List<Student>> response = studentProxy.getAllStudent(authorizationToken, uri);
if (response.getStatusCode().value() == 200)
return response.getBody();
return Collections.emptyList();
}
How to post some data using the open feign client
Just like earlier I said we can use all the HTTp methods in the open feign client, it's time to understand if we want to send data to the API and how we will be able to achieve this in the open feign client. Let's take a look below.
Create an abstract method in the feign client interface and annotate that method with @PostMapping
this time, and add parameters to send the data in the request body of the API. Here we are sending the Student object to add students.
@FeignClient(value = "student", url="${student.base-url}")
public interface StudentProxy
{
@PostMapping("add-student")
ResponseEntity<Long> addStudent(Student student);
}
Your service class may look like the one below to pass the student object.
public Long addStudent()
{
ResponseEntity<Long> response = studentProxy.addStudent(new Student("Jack", "abram"));
if (response.getStatusCode().value() == 200)
return response.getBody();
return 0L;
}
Just like this, you can also leverage the @RequestParam
and the @PathVaraible
. If you want me to explain how to use these two annotations in the open feign client, then you can comment in this post, and I'll be happy to add to this.
Logging in to an open feign client request and response
Debugging is a crucial part of development. It helps us monitor the request flow of the API and find out the exact root cause of the issue. You can also dubug your API request in the open feign client.
Enabling logging from the application.properties file
Let's understand what an open-feign client offers us to debug our API request. First, we must enable debug mode in the application. properties file, following is the syntax
logging.level.<project-package> = DEBUG(code-box)
logging.level.in.learnjavaskills.openfeignclient = DEBUG(code-box)
Configure the level of the log
Once we have successfully enabled logging, we need to configure the level of the log by creating a bean, just like below.
package in.learnjavaskills.openfeignclient;
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class OpenFeignClientConfig
{
@Bean
public Logger.Level feignLoggerLevel()
{
return Logger.Level.FULL;
}
}
There are four levels of log in the open feign client log configuration. In the above example, we have enabled FULL log. Let's dive into the deepest level of the log.
NONE:
This level of log is enabled by default. As the name indicates, none means no log will be printed on the console.
BASIC:
If you want to print only the basic log in the console, such as the request url, response status, and HTTP method used in the API, or you want to monitor the execution time, then the BASIC logging level is best for your case.
HEADERS:
The HEADER level will enable the basic level and the request and response API header details to be printed in the console. This can be a good option to enable in production, but if you are happy with the basic details, then I highly encourage you to opt for the BASIC level only.
FULL:
The final level is FULL, It will print all the request and response flows of the API. This level is best if you are in the development phase.
Let's see the console how logs are populated
Time stamp with date DEBUG 12116 --- [ main] i.l.openfeignclient.StudentProxy : [StudentProxy#addStudent] ---> POST http://localhost:5001/api/student/add-student HTTP/1.1
Time stamp with date DEBUG 12116 --- [ main] i.l.openfeignclient.StudentProxy : [StudentProxy#addStudent] Content-Length: 46
Time stamp with date DEBUG 12116 --- [ main] i.l.openfeignclient.StudentProxy : [StudentProxy#addStudent] Content-Type: application/json
Time stamp with date DEBUG 12116 --- [ main] i.l.openfeignclient.StudentProxy : [StudentProxy#addStudent]
Time stamp with date DEBUG 12116 --- [ main] i.l.openfeignclient.StudentProxy : [StudentProxy#addStudent] {"id":0,"firstname":"Jack","lastname":"abram"}
Time stamp with date DEBUG 12116 --- [ main] i.l.openfeignclient.StudentProxy : [StudentProxy#addStudent] ---> END HTTP (46-byte body)
Time stamp with date DEBUG 12116 --- [ main] i.l.openfeignclient.StudentProxy : [StudentProxy#addStudent] <--- HTTP/1.1 200 (25ms)
Time stamp with date DEBUG 12116 --- [ main] i.l.openfeignclient.StudentProxy : [StudentProxy#addStudent] connection: keep-alive
Time stamp with date DEBUG 12116 --- [ main] i.l.openfeignclient.StudentProxy : [StudentProxy#addStudent] content-type: application/json
Time stamp with date DEBUG 12116 --- [ main] i.l.openfeignclient.StudentProxy : [StudentProxy#addStudent] keep-alive: timeout=60
Time stamp with date DEBUG 12116 --- [ main] i.l.openfeignclient.StudentProxy : [StudentProxy#addStudent] transfer-encoding: chunked
Time stamp with date DEBUG 12116 --- [ main] i.l.openfeignclient.StudentProxy : [StudentProxy#addStudent]
Time stamp with date DEBUG 12116 --- [ main] i.l.openfeignclient.StudentProxy : [StudentProxy#addStudent] 10
Time stamp with date DEBUG 12116 --- [ main] i.l.openfeignclient.StudentProxy : [StudentProxy#addStudent] <--- END HTTP (2-byte body)
Conclusion
In this tutorial, we have seen how to use the open feign client to consume the Rest API, and we have also learned how to enable the log level in the feign client to debug or monitor API requests and the response.
We have discussed in detail all the annotations that we can use in the opein feign client. I'll strongly suggest you do a hand on it to understand each and every topic in this article in detail.
Feel free to comment on this article about your thoughts or opinions; I'll be happy to address that.
Keep learning and keep growing.