Wednesday, June 4, 2025

MS - Call rest service to service call Step3; Using Feign Client

 In Step 2 we Autowired RestTemplate with Load balancing with Discovery client capabilities.

Step 1

Step-3

Here we still has an issue, although we have configured service-discovery, load balancing 

    Can we make it more abstract

    Feign Client is the solution

Let's look at the needs and configurations


1. Add Feign Client dependency. 

    1.1 Open feign dependency, additionally Web and also discovery-client etc as needed.

2. Create an interface to configure url and method

3. Autowire the object of the interface and call the method. Here we do it in the Controller itself for time being. Better to do it in the service layer @Service


Code Blocks and Configurations

POM

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


Create Interface

package com.example.rest_client_with_feign.client;

import com.example.rest_client_with_feign.model.Greet;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

/**
* 1. Define interface with suitable name
* 2. Declare method as needed
*/
@FeignClient("greet-service")
public interface GreetClient {

@GetMapping("/greet")
public Greet greet();
}


Controller with Feign Client Interface Autowired

package com.example.rest_client_with_feign.controller;


import com.example.rest_client_with_feign.client.GreetClient;
import com.example.rest_client_with_feign.model.Greet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class GreetClientController {

//Step 1
// private RestTemplate restTemplate = new RestTemplate();
//Ste 2 - 1. define bean in main class , add @LoadBalanced
// @Autowired
// private RestTemplate restTemplate;

//Step-3
@Autowired
GreetClient greetClient;

/*
@RequestMapping("/greet-call")
Greet greet() {
//Phase 1 - without Discovery/Eureka client
// Greet greet = restTemplate.getForObject("http://localhost:8090/greet", Greet.class);

//Step 2
//With Discovery client - Now we can use service name - and with other option available via eureka server
// you need to configure DiscoveryClient - else cannot find hte hostname "greet-service" which is registered inEureka server
Greet greet = restTemplate.getForObject("http://greet-service/greet", Greet.class);
return greet;
}


*/

/**
* Step 3 :Using Feign Client
* @return
*/
@RequestMapping("/greet-call")
Greet greet() {
//simple mthod call - compare with above step 2
return greetClient.greet();
}
}

Application properties

spring.application.name=rest-client-with-feign
#spring.application.name=rest-client
server.port=8095

#phase 2 - add eureka
eureka.client.serviceUrl.defaultZone= http://localhost:8761/eureka/

you can test with 

1. make sure greet-service , eureka server up and running. Eureka is optional

2. http://localhost:8095/greet-call

This is a simple Summary to understand

----------------------------------------------------------------------------------------------------------

For more information you can refer below

1. Create a Spring Boot Project

Use Spring Initializr with these dependencies:

  • Spring Web

  • OpenFeign

  • Lombok (optional for boilerplate reduction)


2. Enable Feign Clients

Add @EnableFeignClients to your main application class:

java
Copy
Download
@SpringBootApplication
@EnableFeignClients
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

3. Define Feign Client Interface

Create an interface annotated with @FeignClient:

java
Copy
Download
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@FeignClient(name = "user-service", url = "https://jsonplaceholder.typicode.com")
public interface UserClient {
    
    @GetMapping("/users/{id}")
    UserResponse getUserById(@PathVariable("id") Long id);
}

4. Create DTO (Data Transfer Object)

Define the response model (JSON structure must match the API response):

java
Copy
Download
import lombok.Data;

@Data
public class UserResponse {
    private Long id;
    private String name;
    private String email;
    private String phone;
}

5. Use Feign Client in a Service

Inject the Feign client and use it:

java
Copy
Download
import org.springframework.stereotype.Service;

@Service
public class UserService {
    private final UserClient userClient;

    public UserService(UserClient userClient) {
        this.userClient = userClient;
    }

    public UserResponse getUser(Long id) {
        return userClient.getUserById(id);
    }
}

6. Create a REST Controller

Expose an endpoint to trigger the Feign client:

java
Copy
Download
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/users")
public class UserController {
    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/{id}")
    public UserResponse getUser(@PathVariable Long id) {
        return userService.getUser(id);
    }
}

7. Configure Application Properties

Add this to application.properties:

properties
Copy
Download
# Enable Feign logging (optional)
logging.level.org.springframework.cloud.openfeign=DEBUG
feign.client.config.default.loggerLevel=full

# Timeout configuration (example)
feign.client.config.default.connectTimeout=5000
feign.client.config.default.readTimeout=5000

8. Test the Application

  1. Start the Spring Boot app

  2. Access: http://localhost:8080/api/users/1

  3. Expected JSON response:

json
Copy
Download
{
  "id": 1,
  "name": "Leanne Graham",
  "email": "Sincere@april.biz",
  "phone": "1-770-736-8031 x56442"
}

Key Configuration Options:

  1. Multiple Clients:

    java
    Copy
    Download
    @FeignClient(name = "payment-service", url = "${payment.api.url}")
  2. Error Handling:

    java
    Copy
    Download
    @FeignClient(name = "user-service", configuration = CustomErrorDecoder.class)
  3. Request Interceptors (for headers/auth):

    java
    Copy
    Download
    @Bean
    public RequestInterceptor requestInterceptor() {
        return template -> template.header("Authorization", "Bearer TOKEN");
    }

Troubleshooting Tips:

  • Ensure @EnableFeignClients is present

  • Verify the base URL matches the target service

  • Check DTO fields match the API response exactly

  • Use @RequestMapping instead of @GetMapping for complex requests

  • Add @EnableAutoConfiguration if using older Spring versions


No comments:

Post a Comment