Configure Feign Client in Spring Boot
In this article, we’ll learn how to configure a FeignClient in your Spring Boot project to consume RESTFul APIs from other services.
Overview
FeignClient is a Declarative REST Client in Spring Boot Web Application. Declarative REST Client means you just give the client specification as an Interface and spring boot takes care of the implementation for you.
FeignClient is used to consume RESTFul API endpoints exposed by thirdparty or microservice.
Feign vs RestTemplate
It is an alternative of RestTemplate and has following advantages over RestTemplate:-
- Do not need to write implementation classes to call other sevices, just provide specification as an Interface
- Client configurations such as encoding/decoding, timeout, logging can just be done through properties files
- Developed by Netflix. Has great support to work with other spring-boot cloud libraries such as Hystrix, Eureka and Ribbon
- Easy to mock and write test cases
Project Setup
For initial setup of your Spring Boot project, you should use Spring Initializr. Choose the OpenFeign and Spring Web as dependencies. If you are not sure, click the below link for pre-selected required dependencies:-
Also Read here How to setup a Spring Boot project using Spring Initalizr
If you build project as maven. A typical pom.xml for a web project looks like this:-
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.0.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>api</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-api</name>
<description>Create Feign Client to consume RESTFul APIs</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR4</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Similarly build.gradle for a gradle web project:-
build.gradle
dependencies {
implementation "org.springframework.boot:spring-boot-starter-web"
implementation "org.springframework.cloud:spring-cloud-starter-openfeign"
}
Enable Feign Client
When you are working with spring boot project, you have nothing much to do to enable FeignClient for your project. Make sure:-
- You have
spring-boot-starter-web
andspring-cloud-starter-openfeign
dependencies in your pom.xml or build.gradle - You are using
@SpringBootApplication
and@EnableFeignClients
annotations at your application starter class fileApiApplication
.
Spring Boot is opinionated, when it sees the web and openfeign dependencies in the classpath, it sets up all the necessary default configuration required for FeignClient and automatically scans for the classes annotated with @FeignClient
package com.example.api;
@SpringBootApplication
@EnableFeignClients
public class ApiApplication {
public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
}
}
Create Feign Client
Next, we are going to create a FeignClient to consume from RESTFul API endpoint. Let’s create a PostFeignClient
interface -
- Annotated with
@FeignClient
which auto scan by spring boot application to generate feign client - This FeignClient consumes the APIs from this URL: https://jsonplaceholder.typicode.com/
package com.example.api.client;
@FeignClient(name = "postFeignClient", url = "https://jsonplaceholder.typicode.com")
public interface PostFeignClient {
@GetMapping("/posts")
List<Post> getAllPosts();
@GetMapping("/posts/{postId}")
Post getPostById(@PathVariable Long postId);
@GetMapping("/posts")
List<Post> getPostByUserId(@RequestParam Long userId);
@PostMapping("/posts")
Post createPost(Post post);
@PutMapping("/posts")
Post updatePost(Post post);
@DeleteMapping("/posts/{postId}")
Post deletePost(@PathVariable Long postId);
}
Feign Client Configuration
From Property file
Spring boot comes with default global configurations which are applied to all the feign clients you create. Good things is you can change these global configurations from property file such as connection timeout, read timeout and logger level
application.yml
feign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 5000
loggerLevel: BASIC
You can also configure each feign client individually from property file using feign client name or value. Remember, we created our feign client with name @FeignClient(name = "postFeignClient", ...)
Following properties can be configured for each feign client using name or value (e.g. postFeignClient):-
feign:
client:
config:
postFeignClient:
connectTimeout: 5000
readTimeout: 5000
loggerLevel: FULL
errorDecoder: com.example.SimpleErrorDecoder
retryer: com.example.SimpleRetryer
requestInterceptors:
- com.example.FooRequestInterceptor
- com.example.BarRequestInterceptor
decode404: false
encoder: com.example.SimpleEncoder
decoder: com.example.SimpleDecoder
contract: com.example.SimpleContract
From Configuration Class file
We can also configure a FeignClient using a Configuration class. You have to pass this class as configuration while creating FeignClient e.g. @FeignClient(configuration = "FeignClientConfig.class", ...)
package com.example.api.client;
import com.example.api.config.FeignClientConfig;
@FeignClient(name = "postFeignClient", configuration = FeignClientConfig.class, url = "https://jsonplaceholder.typicode.com")
public interface PostFeignClient {}
In FeignClientConfig
, you can create beans of Decoder
, Encoder
, Logger
, Contract
, Feign.Builder
and Client
to override default beans created by Spring Boot. You can also create beans of Logger.Level
, Retryer
, ErrorDecoder
and RequestInterceptor
to include these features.
package com.example.api.config;
public class FeignClientConfig {}
Spring Cloud Netflix provides the following beans by default for feign (BeanType beanName: ClassName):
Decoder
feignDecoder:ResponseEntityDecoder
(which wraps aSpringDecoder
)Encoder
feignEncoder:SpringEncoder
Logger
feignLogger:Slf4jLogger
Contract
feignContract:SpringMvcContract
Feign.Builder
feignBuilder:HystrixFeign.Builder
Client
feignClient: if Ribbon is enabled it is aLoadBalancerFeignClient
, otherwise the default feign client is used.
Spring Cloud Netflix does not provide the following beans by default for feign, but still looks up beans of these types from the application context to create the feign client:
Logger.Level
Retryer
ErrorDecoder
Request.Options
RequestInterceptor
SetterFactory
Request Interceptor
Feign provides RequestInterceptor
interface that can be used for adding/removing/mutating any part of the request.
public interface RequestInterceptor {
/**
* Called for every request. Add data using methods on the supplied RequestTemplate.
*/
void apply(RequestTemplate template);
}
All we need to do is to create a Bean of type RequestInterceptor inside a config class and provide that configuration to FeignClient.
package com.example.api.config;
public class FeignClientConfig {
/**
* Enable this bean if you want to add headers in HTTP request
*/
@Bean
public RequestInterceptor requestInterceptor() {
return requestTemplate -> {
requestTemplate.header("Content-Type", "application/json");
requestTemplate.header("Accept", "application/json");
requestTemplate.header("header_1", "value_1");
requestTemplate.header("header_2", "value_2");
requestTemplate.header("header_3", "value_3");
};
}
/**
* Enable this bean if you want to add basic Authorization header
* for e.g. Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
*/
@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
return new BasicAuthRequestInterceptor("username", "password");
}
}
Note:- Do not annotate this class with @Configuration
annotation, otherwise this configuration will become global i.e. all FeignClients will inherit this config in that case.
Once you apply this configuration to FeignClient, all the requests made by that FeignClient will include the common headers and basic authorization header to outgoing HTTP requests.
Feign Logging
A logger is created for each FeignClient by default. Feign logging only responds to the DEBUG level.
To enable the feign logging for all the feign clients, declare the logging level of package name of client interfaces to DEBUG:-
logging:
level:
com.example.api.client: DEBUG
To enable the feign logging for specific FeignClient, declare the logging level to that interface to DEBUG:-
logging:
level:
com.example.api.client.PostFeignClient: DEBUG
Once you enable the feign logging by setting the logging level to DEBUG, you can further control the logging using loggerLevel configuraton property which tells Feign how much to log per request. Choices are:
- NONE, No logging (DEFAULT).
- BASIC, Log only the request method and URL and the response status code and execution time.
- HEADERS, Log the basic information along with request and response headers.
- FULL, Log the headers, body, and metadata for both requests and responses.
For e.g. BASIC loggerLevel for all feign clients and FULL loggerLevel for postFeignClient:-
feign:
client:
config:
default:
loggerLevel: BASIC
postFeignClient:
loggerLevel: FULL
Consume and Test Feign Client
Now since we have created feign client, let’s create a service layer class PostService
and its implementation PostServiceImpl
to consume these APIs using the feign client
public interface PostService {
List<Post> getAllPosts();
Post getPostById(Long postId);
List<Post> getAllPostsByUserId(Long userId);
Post createPost(Post post);
void updatePost(Long postId, Post post);
void deletePost(Long postId);
}
@Service
public class PostServiceImpl implements PostService {
@Autowired
private PostFeignClient postFeignClient;
@Override
public List<Post> getAllPosts() {
return postFeignClient.getAllPosts();
}
@Override
public Post getPostById(Long postId) {
return postFeignClient.getPostById(postId);
}
@Override
public List<Post> getAllPostsByUserId(Long userId) {
return postFeignClient.getPostByUserId(userId);
}
@Override
public Post createPost(Post post) {
return postFeignClient.createPost(post);
}
@Override
public void updatePost(Long postId, Post post) {
postFeignClient.updatePost(post);
}
@Override
public void deletePost(Long postId) {
postFeignClient.deletePost(postId);
}
}
Now since we have created our service class and consumed APIs using feign client. Let’s create a controller PostController
to test our feign client.
@RestController
@RequestMapping("/posts")
public class PostController {
@Autowired
private PostService postService;
@GetMapping
public List<Post> getAllPosts() { return postService.getAllPosts(); }
@GetMapping("/{postId}")
public Post getPostById(@PathVariable Long postId) { return postService.getPostById(postId); }
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Post createPost(Post post) { return postService.createPost(post); }
@PutMapping("/{postId}")
@ResponseStatus(HttpStatus.OK)
public void updatePost(@PathVariable Long postId, Post post) { postService.updatePost(postId, post); }
@DeleteMapping("/{postId}")
@ResponseStatus(HttpStatus.OK)
public void deletePost(@PathVariable Long postId) { postService.deletePost(postId); }
}
Let’s test our controller endpoint from the browser to see if Feign client is working.
That’s it. You have successfully created and tested feign client to consume APIs from given endpoint.
Download the complete source code for creating feign client from github/springboot-api