Spring Cloud OpenFeign is capable of communicating with third-party REST API and commonly used with Spring Boot. In this tutorial, we are going to explain how we can use feign client to consume third-party REST API with multiple HTTP methods including GET, POST, DELETE, PATCH.

Here I’m going to show how we consume REST API using feign client in Spring Boot. If you are new to spring boot follow How to Create a Spring Boot Project, which written by us first, and get familiar with spring boot first.

If you are really new to feign client, check out our article on How to Use Feign Client in Spring Boot first to understand how we can configure feign into spring boot application and get a basic understanding.

We are using the third party fake API with pagination to consume using feign client. This API is hosted and open to consume for free. There are multiple API endpoints that cover all the HTTP methods.

Technologies Going to Use,

  • Java 1.8
  • Spring Boot: 2.3.4.RELEASE
  • Spring Cloud OpenFeign
  • Lombok
  • Gradle
  • Intellij Idea for IDE

Main topics we are going to discuss here,

Adding Required Dependencies

spring initializr to generate a spring boot project with all the dependencies I need for this tutorial. If you are really new to Spring Boot, Please follow our article on How to Create a Spring Boot Project.

Here I’ve selected following dependencies to create spring boot project using spring initilizr,

  • Spring Web – contains common web-specific utilities for both Servlet and Portlet environments.
  • Spring Cloud OpenFeign –  OpenFeign integrations for Spring Boot apps.
  • Lombok – The coolest plugin to spicing up your java. Never write another getter or equals method again, with one annotation your class has a fully-featured builder, Automate your logging variables, and much more.

If you need to learn how we can use Lombok in spring boot follow our article Guide to use Lombok In Spring Boot.

Consuming GET Endpoint Using Feign Client

Here we are calling GET API to which returns array of airlines. Hence we need to map it into List of AirlineResponse. AirlineResponse include mapping for every param coming as single Airline from API.

API URL : https://api.instantwebtools.net/v1/airlines

Response JSON

[
    {
        "id": 1,
        "name": "Quatar Airways",
        "country": "Quatar",
        "logo": "https://upload.wikimedia.org/wikipedia/en/thumb/9/9b/Qatar_Airways_Logo.svg/300px-Qatar_Airways_Logo.svg.png",
        "slogan": "Going Places Together",
        "head_quaters": "Qatar Airways Towers, Doha, Qatar",
        "website": "www.qatarairways.com",
        "established": "1994"
    },
    {
        "id": 2,
        "name": "Singapore Airlines",
        "country": "Singapore",
        "logo": "https://upload.wikimedia.org/wikipedia/en/thumb/6/6b/Singapore_Airlines_Logo_2.svg/250px-Singapore_Airlines_Logo_2.svg.png",
        "slogan": "A Great Way to Fly",
        "head_quaters": "Airline House, 25 Airline Road, Singapore 819829",
        "website": "www.singaporeair.com",
        "established": "1947"
    }
]

AirlineResponse class which maps every param inside above JSON.

package com.javatodev.api.rest.response;

import lombok.Data;

@Data
public class AirlineResponse {
    private Long id;
    private String name;
    private String country;
    private String logo;
    private String slogan;
    private String head_quaters;
    private String website;
    private String established;
}

Here I’m using JSON to Java Converter with Lombok support from instantwebtools.net. This tool allows us to generate Java POJO with Lombok annotations as we need.

Ok, now we are ready to write our feign client method to consume GET endpoint. To do that just add the following line into your feign client. After that feign will do the communication with third party API and return the response you need.

@RequestMapping(method = RequestMethod.GET, value = "/airlines")
    List<AirlineResponse> readAirLines();

Reading GET API with PathVariable

Path variables are used to read specific item from a API. eg:- Read data using a ID. Here we are going to read GET endpoint with a path variable.

API URL : https://api.instantwebtools.net/v1/airlines/1

Response JSON

{
    "id": 1,
    "name": "Quatar Airways",
    "country": "Quatar",
    "logo": "https://upload.wikimedia.org/wikipedia/en/thumb/9/9b/Qatar_Airways_Logo.svg/300px-Qatar_Airways_Logo.svg.png",
    "slogan": "Going Places Together",
    "head_quaters": "Qatar Airways Towers, Doha, Qatar",
    "website": "www.qatarairways.com",
    "established": "1994"
}

Here the API returns only one object for the given ID. So we just need to add AirlineResponse as the return type for the API call. Additionally, to set path variables we could use @PathVariable annotation, and don’t forget to use the same variable name for definition.

@RequestMapping(method = RequestMethod.GET, value = "/airlines/{airlineId}")AirlineResponse readAirLineById(@PathVariable("airlineId") String airlineId);

Same thing could be done as follow,

@RequestMapping(method = RequestMethod.GET, value = "/airlines/{airlineId}")AirlineResponse readAirLineById(@PathVariable String airlineId);

As you can see here airlineId is the same param we are passing through the API definition. So Feign capture correct value even without defining airlineId as @PathVariable in above example. Both examples are working correctly.

Reading Paginated API Endpoint

Pagination is used to read heavy data set by smaller chunks. So with feign client we could read paginated API as follows.

In here specialty with pagination is we need to send a few more params to read while comparing to normal GET API. Those are page number, page size, and more.

API URL: https://api.instantwebtools.net/v1/passenger?page=0&size=2

Response JSON:

{
    "totalPassengers": 1017,
    "totalPages": 509,
    "data": [
        {
            "_id": "5f1c59c3fa523c3aa793bce0",
            "name": "Mike Tyso",
            "trips": 183,
            "airline": {
                "id": 7,
                "name": "Deutsche Lufthansa AG",
                "country": "Germany",
                "logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b8/Lufthansa_Logo_2018.svg/300px-Lufthansa_Logo_2018.svg.png",
                "slogan": "Say yes to the world",
                "head_quaters": "Cologne, Germany",
                "website": "lufthansa.com",
                "established": "1953"
            },
            "__v": 0
        },
        {
            "_id": "5f1c59c3fa523c3aa793bce5",
            "name": " Türkiye 1",
            "trips": 818,
            "airline": {
                "id": 2,
                "name": "Singapore Airlines",
                "country": "Singapore",
                "logo": "https://upload.wikimedia.org/wikipedia/en/thumb/6/6b/Singapore_Airlines_Logo_2.svg/250px-Singapore_Airlines_Logo_2.svg.png",
                "slogan": "A Great Way to Fly",
                "head_quaters": "Airline House, 25 Airline Road, Singapore 819829",
                "website": "www.singaporeair.com",
                "established": "1947"
            },
            "__v": 0
        }
    ]
}

In this API it returns a list of passengers along with 2 more additional parameters for total pages and total number of results.

Let’s write classes set to map the response from paginated API.

package com.javatodev.api.rest.response;

import java.util.List;

import lombok.Data;

@Data
public class PaginatedPassengerResponse {
    private Long totalPassengers;
    private Long totalPages;
    private List<PassengerResponse> data;
}
package com.javatodev.api.rest.response;

import com.fasterxml.jackson.annotation.JsonProperty;

import lombok.Data;

@Data
public class PassengerResponse {
    @JsonProperty(value = "_id")
    private String id;
    private String name;
    private Long trips;
    private AirlineResponse airline;
}

Here the API sending id as “_id”, hence I’m using @JsonProperty to map _id with id variable in java.

Ok, now we are ready to read paginated API using feign client. We just need to add the following to feign client for achieve that.

@RequestMapping(method = RequestMethod.GET, value = "/passenger?page=0&size=2")
    PaginatedPassengerResponse readPassengers();

But there is a problem with this implementation since the page number and page size is hard coded. We can pass dynamic Request parameters in this scenario as below.

@RequestMapping(method = RequestMethod.GET, value = "/passenger")
    PaginatedPassengerResponse readPassengers(@RequestParam("size") Long size, @RequestParam("page") Long page);

Consuming POST API Endpoint Using Feign Client

Normally POST endpoints are exposed to send dataset as a request body in order to persist or process in the API backend. So mainly the base difference between GET and POST is POST have a request body while GET doesn’t.

So with Feign client we could send the body with POST request as follows.

API URL: https://api.instantwebtools.net/v1/airlines – POST

Request Body:

{
    "id": 12,
    "name": "Sri Lankan Airways",
    "country": "Sri Lanka",
    "logo": "https://upload.wikimedia.org/wikipedia/en/thumb/9/9b/Qatar_Airways_Logo.svg/sri_lanka.png",
    "slogan": "From Sri Lanka",
    "head_quaters": "Katunayake, Sri Lanka",
    "website": "www.srilankaairways.com",
    "established": "1990"
}

Airline Creation Request:

package com.javatodev.feign.rest.request;

import lombok.Data;

@Data
public class AirlineCreateRequest {
    private Long id;
    private String name;
    private String country;
    private String logo;
    private String slogan;
    private String head_quaters;
    private String website;
    private String established;
}

Here we can implement feign client for POST request as follows.

@RequestMapping(method = RequestMethod.POST, value = "/airlines")
    AirlineResponse createAirline(@RequestBody AirlineCreateRequest airlineCreateRequest);

Requesting DELETE API with Feign Client

Here normally REST API needs to have an identification to grab whatever the data on database to delete. Hence we can accomplish that with passing an ID along with the request over URL. But in advance case there could be a scenrio where we need to send request body with multiple params to identify the record which we need to delete.

Here I’m showing how we can call DELETE API with and ID to delete a record from a database.

API URL : https://api.instantwebtools.net/v1/passenger/1 – DELETE

@RequestMapping(method = RequestMethod.DELETE, value = "/passenger/{passengerId}")
    String deletePassenger(@PathVariable String passengerId);

Calling PATCH API Endpoint with Feign Client

PATCH requests are used to update a record or a record set in a REST API. Normally PATCH API have a Request Body to send whatever the params could be updated and identification to identify the correct data set from the database.

This API only allows to update passenger name.

API URL : https://api.instantwebtools.net/v1/passenger/1 – DELETE

Request Body:

{    
  "name": "John Doe"
}

Important: Spring Cloud OpenFeign doesn’t support for PATCH requests by default.

Hence if we try to write a PATCH request call, It will be rejected saying ‘java.net.ProtocolException: Invalid HTTP method: PATCH’. – Request processing failed; nested exception is feign.RetryableException: Invalid HTTP method: PATCH executing

So What we can do to resolve this issue. Here we can override the default feign client with feign-okhttp which supports for PATCH request.

To do that we should add feign-okhttp dependencies into the application by,

implementation 'io.github.openfeign:feign-okhttp:11.0'

Then we should create custom configuration which allows to use okhttp inside spring boot application.

package com.javatodev.api.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import feign.okhttp.OkHttpClient;

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

Then we should introduce this CustomFeignClientConfiguration as a configuration for our Feign client which was written earlier.

we can do that by adding the following to the API client interface.

@FeignClient(value = "${app.feign.config.name}", url = "${app.feign.config.url}", configuration = CustomFeignClientConfiguration.class)

Ok, Now our application is ready to call PATCH API using feign client. Just add following PATCH call to the client we defined.

@RequestMapping(method = RequestMethod.PATCH, value = "/passenger/{passengerId}")
String updatePassenger(@PathVariable String passengerId, @RequestBody PassengerUpdateRequest request);

Conclusion

In this article, we’ve discussed Consuming REST API Using Feign Client in Spring Boot covering CRUD operations in API, along with few more additional configurations, value additions into the feign client setup with spring boot.

You can find source codes for this tutorial from our Github.