Spring Cloud Feign Example

author-image   By Dhiraj,   26 June, 2019 4K

In this tutorial, we will learn about creating web service clients with Feign in a spring cloud application with an example for REST based HTTP calls. We will be developing 2 different microservices as customer-service and product-service. These 2 different services will register themselves to Netflix discovery server and will have Feign client integrated with customer-service. The customer-service will act as a client and invoke REST based web service calls to product-service in order to fetch product details. In this example, we will create the HTTP client that can perform all the CRUD operations involved with the product-service.

What is Feign

Feign is a declarative web service client that makes writing web service clients easier. We use the different annotations provided by the Spring framework such as Requestmapping, @PathVariable in a Java interface to define the abstract implementation of our actual API and the Feign internally process these annotations into a templatized request to the actual web service.

Spring Cloud provides out of the box integration with Ribbon and Eureka while using Feign. In this implementation, we will be using spring-boot 2.1.6.RELEASE and

Feign Integration with Spring Cloud

With maven, we include spring-cloud-starter-openfeign artifact in our pom.xml file annotate the main class with the annotation @EnableFeignClients. Once, this is done we create an interface that has the abstract implementation of our HTTP client and annotate it with @FeignClient. Now, we can inject this anywhere where we want to invoke the REST API and Feign does the remaining part.

Below is a simple example of the interface that we will be creating in a while.

@FeignClient(name="product-service", configuration = CustomFeignConfig.class)
public interface ProductClient {

    @GetMapping("/products")
    List listProducts();

...

We can also override the different Feign defaults such as Decoder, Encoder, Logger, etc. in spring cloud with our custom implementations.

Building Spring Cloud Application

As discussed above, we will have a discovery server, customer-service and product-service. Let us quickly implement these.

Discovery Server Implementation

This implementation is exactly similar to my previous implementation of Spring cloud discovery server. Hence, let me put the snippets directly.

ServiceDiscoveryApplication.java
@EnableEurekaServer
@SpringBootApplication
public class ServiceDiscoveryApplication {

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

}
pom.xml
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
application.yml
spring:
  application:
    name: discovery-service

eureka:
  client:
    eureka-server-connect-timeout-seconds: 5
    enabled: true
    fetch-registry: false
    register-with-eureka: false

server:
  port: 8761

Product Service Implementation

Product service is a very simple implementation that has some REST endpoints exposed. Below is the controller implementation. It has GET and POST implementation. In the later section, we will be creating our Feign client for these endpoints in customer service.

@RestController
@RequestMapping("/products")
public class ProductController {

    @GetMapping
    public List listProducts(){
        return DataStore.listProducts();
    }

    @GetMapping("/{id}")
    private Product getProductById(@PathVariable String id){
        return DataStore.listProducts().stream().filter(prd -> prd.getId().equalsIgnoreCase(id)).findFirst().get();
    }

    @PostMapping
    private Product getProductById(@RequestBody Product product){
        product.setId("PRD " + RandomUtils.nextInt());
        return product;
    }

    @GetMapping("/customer/{custId}")
    public List listProductsByCustomerId(@PathVariable String custId){
        return DataStore.listProducts().stream().filter(product -> product.getCustomerId().equalsIgnoreCase(custId)).collect(Collectors.toList());
    }
}

Below is the POJO for Product.java

public class Product {

    private String id;
    private String name;
    private String price;
    private String customerId;

Below is the util class that has some static dummy data.

DataStore.java
public class DataStore {

    public static List listCustomers(){
        List customers = new ArrayList();
        for(int i = 1; i < 6; i++){
            Customer customer = new Customer();
            customer.setId("CUST" + i);
            customer.setAge(30 + i);
            customer.setName("customer " + i);
            customers.add(customer);
        }
        return customers;
    }

    public static List listProducts(){
        List products = new ArrayList();
        for(int i = 1; i < 6; i++){
            Product product = new Product();
            product.setId("PRD" + i);
            product.setName("Product Name " + 1);
            product.setPrice("USD " + 100 + i);
            product.setCustomerId("CUST" + i);
            products.add(product);
        }
        return products;
    }
}

Customer Service Implementation

It also has a very similar implementation as product service. Let us define our controller first. The below implementation has only 1 endpoint exposed to fetch customer details. To popuate the different products specific to a customer, we need to call the product service API and for this we have Feign client implemented in the customer service. Feign client implementation is in our next section.

CustomerController.java
@RequestMapping("/customers")
@RestController
public class CustomerController {

    @Autowired
    private ProductClient productClient;

    @GetMapping("/{id}")
    public CustomerDto getCustomerById(@PathVariable String id){
        Customer customer = DataStore.listCustomers().stream().filter(cust -> cust.getId().equalsIgnoreCase(id)).findFirst().get();
        List products = productClient.listProductsByCustomerId(id);
        CustomerDto dto = new CustomerDto();
        BeanUtils.copyProperties(customer, dto);
        dto.setProducts(products);
        return dto;
    }

}
Customer.java
public class Customer {

    private String id;
    private String name;
    private int age;
}

CustomerDto includes product details too.

CustomerDto .java
public class CustomerDto extends Customer {

    private List products;
}

Feign Client Implementation

Let us start our implementation of integrating Feign with spring cloud. Below is the dependency for the same.

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

Below is the Feign client implementation. At run-time, product-service will be resolved with a look up in the discovery server. We can have the configurations defined in CustomFeignConfig.java to override the defaults of Feign client such as Decoder, Encoder, Logger, Contract, etc. For now, let us use the default implementations. The implementation has example for GET and POST request with parameterised requests.

package com.devglan.customerservice.feign.client;

import com.devglan.customerservice.feign.config.CustomFeignConfig;
import com.devglan.commons.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import java.util.List;

@FeignClient(name="product-service", configuration = CustomFeignConfig.class)
public interface ProductClient {

    @GetMapping("/products")
    List listProducts();

    @GetMapping("/products/{id}")
    Product getProductById(@PathVariable String id);

    @PostMapping("/products")
    Product create(@RequestBody Product product);

    @GetMapping("/products/customer/{custId}")
    List listProductsByCustomerId(@PathVariable String custId);
}

Below is the application.yml for customer-service.

spring:
  application:
    name: customer-service

server:
  port: 8090
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka

Testing the Application

The above example just works fine. Once all the services are deployed we can hit

http://localhost:8090/customers/CUST1
to get below response which will include the list of products too.

feign-client-api-response

Overriding Feign Defaults

As discussed above, we can override feignDecoder, feignEncoder, feignLogger, feignContract, etc with our custom implementation. To do so, we can define our custom bean definition in the config file that is included in FeignClient annotation - CustomFeignConfig.java. Below is a sample example. This replaces the SpringMvcContract with feign.Contract.Default and adds a RequestInterceptor to the collection of RequestInterceptor.

For more overriding options, you can visit the official page here.

package com.devglan.customerservice.feign.config;

import feign.Contract;
import feign.auth.BasicAuthRequestInterceptor;
import feign.okhttp.OkHttpClient;
import org.springframework.context.annotation.Bean;

public class CustomFeignConfig {

    @Bean
    public Contract feignContract() {
        return new feign.Contract.Default();
    }

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

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

Conclusion

In this article, we created integrated Feign client with spring cloud and developed an example application using discovery server to demonstrate the same. The source code can be found here on github.

If You Appreciate This, You Can Consider:

  • Like us at: Facebook or follow us at Twitter
  • Share this article on social media or with your teammates.
  • We are thankful for your never ending support.

About The Author

author-image

I am an energetic professional who enjoys the challenges involved in working with people and resolving real-time problems. Technical expertise in building highly scalable, distributed and self-healing cloud applications. Technical Skills: Java/J2EE, Spring Framework, Hibernate, Angular, Reactive Programming, Microservices, Rest APIs, Kafka, ELK, etc.

Further Reading on Spring Cloud

1 Spring Cloud Tutorial

2 Refresh Property Config Runtime

3 Spring Cloud Netflix Eureka

4 Introduction To Microservices

5 Encrypt Decrypt Cloud Config Properties

6 Spring Webflux Rest Authentication