1. Overview

In this tutorial, we’re going to describe Spring Cloud OpenFeign — a declarative REST client for Spring Boot apps.

Feign makes writing web service clients easier with pluggable annotation support, which includes Feign annotations and JAX-RS annotations.

Also, Spring Cloud adds support for Spring MVC annotations and for using the same HttpMessageConverters as used in Spring Web.

One great thing about using Feign is that we don’t have to write any code for calling the service, other than an interface definition.

2. Dependencies

First, we’ll start by creating a Spring Boot web project and adding the spring-cloud-starter-openfeign dependency to our pom.xml file:

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

Also, we’ll need to add the spring-cloud-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>

We can find the latest versions of spring-cloud-starter-openfeign and spring-cloud-dependencies on Maven Central.

3. Feign Client

Next, we need to add @EnableFeignClients to our main class:

@SpringBootApplication
@EnableFeignClients
public class ExampleApplication {

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

With this annotation, we enable component scanning for interfaces that declare they are Feign clients.

Then we declare a Feign client using the @FeignClient annotation:

@FeignClient(value = "jplaceholder", url = "https://jsonplaceholder.typicode.com/")
public interface JSONPlaceHolderClient {

    @RequestMapping(method = RequestMethod.GET, value = "/posts")
    List<Post> getPosts();

    @RequestMapping(method = RequestMethod.GET, value = "/posts/{postId}", produces = "application/json")
    Post getPostById(@PathVariable("postId") Long postId);
}

In this example, we’ve configured a client to read from the JSONPlaceholder APIs.

The value argument passed in the @FeignClient annotation is a mandatory, arbitrary client name, while with the url argument, we specify the API base URL.

Furthermore, since this interface is a Feign client, we can use the Spring Web annotations to declare the APIs that we want to reach out to.

4. Configuration

Now, it’s very important to understand that each Feign client is composed of a set of customizable components.

Spring Cloud creates a new default set on demand for each named client using the FeignClientsConfiguration class that we can customize as explained in the next section.

The above class contains these beans:

  • Decoder – ResponseEntityDecoder, which wraps SpringDecoder, used to decode the Response
  • Encoder – SpringEncoder is used to encode the RequestBody.
  • Logger – Slf4jLogger is the default logger used by Feign.
  • Contract – SpringMvcContract, which provides annotation processing
  • Feign-Builder – HystrixFeign.Builder is used to construct the components.
  • Client – LoadBalancerFeignClient or default Feign client

4.1. Custom Beans Configuration

If we want to customize one or more of these beans, we can override them by creating a Configuration class, which we then add to the FeignClient annotation:

@FeignClient(value = "jplaceholder",
  url = "https://jsonplaceholder.typicode.com/",
  configuration = ClientConfiguration.class)
public class ClientConfiguration {

    @Bean
    public OkHttpClient client() {
        return new OkHttpClient();
    }
}

In this example, we tell Feign to use OkHttpClient instead of the default one to support HTTP/2.

Feign supports multiple clients for different use cases, including the ApacheHttpClient, which sends more headers with the request, for example, Content-Length, which some servers expect.

To use these clients, let’s not forget to add the required dependencies to our pom.xml file:

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
</dependency>

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>

We can find the latest versions of feign-okhttp and feign-httpclient on Maven Central.

4.2. Configuration Using Properties

Rather than use a Configuration class, we can use application properties to configure Feign clients, as shown in this application.yaml example:

feign:
  client:
    config:
      default:
        connectTimeout: 5000
        readTimeout: 5000
        loggerLevel: basic

With this configuration, we’re setting the timeouts to five seconds and the logger level to basic for each declared client in the application.

Finally, we can create the configuration with default as the client name to configure all @FeignClient objects, or we can declare the feign client name for a configuration:

feign:
  client:
    config:
      jplaceholder:

If we have both Configuration bean and configuration properties, configuration properties will override Configuration bean values.

5. Interceptors

Adding interceptors is another useful feature provided by Feign.

The interceptors can perform a variety of implicit tasks, from authentication to logging, for every HTTP request/response.

In this section, we’ll implement our own interceptor, as well as use the one provided by the Spring Cloud OpenFeign out-of-the-box. Both will add a basic authentication header to each request.

5.1. Implementing RequestInterceptor

Let’s implement our custom request interceptor:

@Bean
public RequestInterceptor requestInterceptor() {
  return requestTemplate -> {
      requestTemplate.header("user", username);
      requestTemplate.header("password", password);
      requestTemplate.header("Accept", ContentType.APPLICATION_JSON.getMimeType());
  };
}

Also, to add the interceptor to the request chain, we just need to add this bean to our Configuration class or, as we saw previously, declare it in the properties file:

feign:
  client:
    config:
      default:
        requestInterceptors:
          com.baeldung.cloud.openfeign.JSONPlaceHolderInterceptor

5.2. Using BasicAuthRequestInterceptor

Alternatively, we can use the BasicAuthRequestInterceptor class that the Spring Cloud OpenFeign provides:

@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
    return new BasicAuthRequestInterceptor("username", "password");
}

It’s simple as that. Now all the requests will contain the basic authentication header.

6. Hystrix Support

Feign supports Hystrix, so if we have enabled it, we can implement the fallback pattern.

With the fallback pattern, when a remote service call fails, rather than generating an exception, the service consumer will execute an alternative code path to try to carry out the action through another means.

To achieve the goal, we need to enable Hystrix by adding feign.hystrix.enabled=true in the properties file.

This allows us to implement fallback methods that are called when the service fails:

@Component
public class JSONPlaceHolderFallback implements JSONPlaceHolderClient {

    @Override
    public List<Post> getPosts() {
        return Collections.emptyList();
    }

    @Override
    public Post getPostById(Long postId) {
        return null;
    }
}

To let Feign know that fallback methods have been provided, we also need to set our fallback class in the @FeignClient annotation:

@FeignClient(value = "jplaceholder",
  url = "https://jsonplaceholder.typicode.com/",
  fallback = JSONPlaceHolderFallback.class)
public interface JSONPlaceHolderClient {
    // APIs
}

7. Logging

For each Feign client, a logger is created by default.

To enable logging, we should declare it in the application.properties file using the package name of the client interfaces:

logging.level.com.baeldung.cloud.openfeign.client: DEBUG

Or, if we want to enable logging only for one particular client in a package, we can use the full class name:

logging.level.com.baeldung.cloud.openfeign.client.JSONPlaceHolderClient: DEBUG

Note that Feign logging responds only to the DEBUG level.

The Logger.Level that we may configure per client indicates how much to log:

public class ClientConfiguration {
    
    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.BASIC;
    }
}

There are four logging levels to choose from:

  • NONE – no logging, which is the default
  • BASIC – log only the request method, URL and response status
  • HEADERS – log the basic information together with request and response headers
  • FULL – log the body, headers and metadata for both request and response

8. Error Handling

Feign’s default error handler, ErrorDecoder.default, always throws a FeignException.

Now, this behavior isn’t always the most useful. So, to customize the Exception thrown, we can use a CustomErrorDecoder:

public class CustomErrorDecoder implements ErrorDecoder {
    @Override
    public Exception decode(String methodKey, Response response) {

        switch (response.status()){
            case 400:
                return new BadRequestException();
            case 404:
                return new NotFoundException();
            default:
                return new Exception("Generic error");
        }
    }
}

Then, as we’ve done previously, we have to replace the default ErrorDecoder by adding a bean to the Configuration class:

public class ClientConfiguration {

    @Bean
    public ErrorDecoder errorDecoder() {
        return new CustomErrorDecoder();
    }
}

9. Conclusion

In this article, we discussed Spring Cloud OpenFeign and its implementation in a simple sample application.

We’ve also seen how to configure a client, add interceptors to our requests and handle errors using Hystrix and ErrorDecoder.

As usual, all code samples shown in this tutorial are available over on GitHub.