Implementing Declarative REST Clients with Spring Cloud Feign

Overview of Spring Cloud Feign

Spring Cloud Feign acts as a declarative HTTP client template designed to simplify interactions between microservices. Developed originally by Netflix, it allows developers to invoke remote services as if they were local methods, abstracting away the underlying HTTP details.

While frameworks like Dubbo utilize binary RPC protocols for internal communication, Feign relies on standard HTTP/RESTful principles. Despite this difference, Feign provides a similar developer experience by masking the complexity of network requests. In complex distributed systems, multiple components often require access to the same downstream services. Without an abstraction layer, developers must manually manage connection pooling, retries, and serialization for every call. Spring Cloud Feign eliminates this boilerplate, allowing you to define service contracts via interfaces and annotations rather than writing explicit client wrappers around Ribbon or RestTemplate.

Module Initialization

To begin implementing a consumer service capable of invoking other microservices, create a new module named feign-gateway within your existing project structure. This module will serve as the client side of the communication chain.

The following pom.xml configuration defines the necessary dependencies. Note the inclusion of spring-cloud-starter-openfeign which triggers the auto-configuration required for the client proxy generation.

<?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 http://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.7.0</version>
        <relativePath/>
    </parent>

    <groupId>com.techsolution</groupId>
    <artifactId>feign-gateway</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <java.version>11</java.version>
        <spring-cloud.version>2021.0.0</spring-cloud.version>
    </properties>

    <dependencies>
        <!-- Core Web Support -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <!-- Service Registration -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        
        <!-- Declarative Client -->
        <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>
        </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>

Application Bootstrap Configuration

Modify the main entry point class to enable the Feign client capabilities. You must annotate the application class with @EnableFeignClients to allow scanning for interfaces marked with @FeignClient. Additionally, ensure service discovery is active using @EnableDiscoveryClient.

package com.techsolution.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients(basePackages = "com.techsolution.api")
public class ServiceInvocationGateway {

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

Defining the Remote Service Interface

Create a Java interface to represent the API contract of the provider service. This interface does not contain implementation logic; it serves solely as metadata for the proxy ganerator.

We will use the @FeignClient annotation here. While historically the value attribute was common, the name attribute is now preferred for specifying the logical service name registered in the service registry. The url attribute remains available for direct IP/Address testing but should generally be empty in production.

package com.techsolution.api;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * Represents the external user service contract.
 */
@FeignClient(name = "user-provider", path = "/users")
public interface UserServiceProxy {

    /**
     * Retrieves a welcome message from the target service.
     */
    @GetMapping("/message")
    String fetchGreeting();
    
    /**
     * Example of parameter passing via URI.
     */
    @GetMapping("/{id}")
    String getUserById(@RequestParam("id") Long id);
}

Key attributes for @FeignClient include:

  • name: The logical service identifier found in the Eureka registry. It is mandatory for service discovery scenarios.
  • path: A prefix applied to all method mappings within this interface.
  • fallback: Defines a fallback class to execute during circuit breaker events or failures.
  • url: Overrides service discovery to send requests to a hardcoded address (useful for debugging).

Integration with in the Controller

To trigger the remote call, inject the defined interface into a Spring Boot controller. Spring automatically handles the wiring of the proxy bean.

package com.techsolution.gateway.controller;

import com.techsolution.api.UserServiceProxy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/gateway")
public class PublicEndpointHandler {

    private final UserServiceProxy userServiceProxy;

    @Autowired
    public PublicEndpointHandler(UserServiceProxy proxy) {
        this.userServiceProxy = proxy;
    }

    @GetMapping("/request-data")
    public String proxyCall() {
        // Delegates the request internally to the provider service
        return userServiceProxy.fetchGreeting();
    }
}

Runtime Configuraton

Update the application.properties file to register the gateway with the Eureka server and assign it a unique port. Ensure the eureka.client.service-url.defaultZone points to your running service registry instance.

spring.application.name=feign-consumer-service
server.port=8082

eureka.instance.prefer-ip-address=true
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/

Execution and Load Balancing Verification

To validate the implementation, perform the following startup sequence:

  1. Launch the Eureka Registry server.
  2. Start at least two instances of the provider service (user-provider) on different ports to simulate high availability.
  3. Start the feign-consumer-service.

Once all services show a UP status in the registry dashboard, navigate to the gateway endpoint defined in the controller (e.g., http://localhost:8082/gateway/request-data). Multiple refreshes will demonstrate that requests are distributed across the available provider instances. This confirms that Spring Cloud Feign integrates transparently with Ribbon for client-side load balancing without requiring additional configuration.

Tags: spring-cloud-feign microservices service-discovery declarative-rest eureka-client

Posted on Mon, 12 Oct 2026 16:11:06 +0000 by rowman