Configure Feign Client in Spring Boot Configure Feign Client in Spring Boot

Page content

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:-

  1. Do not need to write implementation classes to call other sevices, just provide specification as an Interface
  2. Client configurations such as encoding/decoding, timeout, logging can just be done through properties files
  3. Developed by Netflix. Has great support to work with other spring-boot cloud libraries such as Hystrix, Eureka and Ribbon
  4. 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:-

https://start.spring.io/#!type=maven-project&language=java&platformVersion=2.3.0.RELEASE&packaging=jar&jvmVersion=1.8&groupId=com.example&artifactId=api&name=api&description=Create%20Feign%20Client%20to%20consume%20RESTFul%20APIs&packageName=com.example.api&dependencies=cloud-feign,web

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:-

  1. You have spring-boot-starter-web and spring-cloud-starter-openfeign dependencies in your pom.xml or build.gradle
  2. You are using @SpringBootApplication and @EnableFeignClients annotations at your application starter class file ApiApplication.

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 -

  1. Annotated with @FeignClient which auto scan by spring boot application to generate feign client
  2. 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 a SpringDecoder)
  • Encoder feignEncoder: SpringEncoder
  • Logger feignLogger: Slf4jLogger
  • Contract feignContract: SpringMvcContract
  • Feign.Builder feignBuilder: HystrixFeign.Builder
  • Client feignClient: if Ribbon is enabled it is a LoadBalancerFeignClient, 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.

Feign Client Endpoint Testing

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