Getting Started with Feign Client in Spring

22.08.2018 by Jens in Spring Boot

In this tutorial, we will make our first remote call using the FeignClient lib in Spring Boot. We will use the Spring Cloud integration instead of manually creating the client.

FeignClient is a library for creating REST API clients in a declarative way. So, instead of manually coding clients for remote API and maybe using Springs RestTemplate we declare a client definition and the rest is generated during runtime for use.

The Application

We will build a small command line app, which queries flights departing from Frankfurt Airport.

Pre-requisites

In this tutorial, we are going to use the FRAPort API which offers to query flights arriving and departing from Frankfurt Airport (FRA). The API is free and you must register there.

The API requires authentication and you need to create an access token there. It is straightforward and explained in their instruction.

Adding Dependencies

We use Maven for this tutorial. As we do not want to mess with version numbers, the easiest way is to include the Spring Cloud setup in the dependencyManagement in the Maven POM.

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Finchley.SR1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

Now, we can add the dependency to Feign with a classical Spring Boot starter:

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

FeignClient Basics

The Feign client uses a declarative approach for accessing the API. To use it, we must first enable the Spring Cloud support for it on our Spring Boot Application with the @EnableFeignClients annotation at the class level on a @Configuration class.

@SpringBootApplication
@EnableFeignClients
public class FeignTutorialFraportApplication implements CommandLineRunner {
    //omitted
}

Next step is to declare an interface for accessing our API. We name it FlightClient as it will query flights.

@FeignClient(name="FlightClient", url= "https://developer.fraport.de/api/flights/1.0")
public interface FlightClient {

    @RequestMapping(method = RequestMethod.GET, value = "/flight/FRA/departure")
    List<FlightWrapper> getDepartingFlights();


    @RequestMapping(method = RequestMethod.GET, value = "/flightDetails/{airlineCode}/{flightNumber}/")
    List<FlightWrapper> getFlightDetails(
                    @PathVariable("airlineCode") String airlineCode, 
                    @PathVariable("flightNumber") Integer flightNumber
    );

}

To turn it into a Feign client, we must set the @FeignClient annotation on the interface and give it a name with the name attribute and also set the remote URL with the url attribute. SpEL is supported here so we could externalize the values to property files.

To define a remote call, we must declare a method for that and use some annotations from Spring MVC, which are normally used on @Controller on the server side. They act the same, just on the client side now.

    @RequestMapping(method = RequestMethod.GET, value = "/flight/FRA/departure")
    List<FlightWrapper> getDepartingFlights();

This defines a method getDepartingFlights which will return a List of FlightWrapper, a simple POJO. The code for the POJO will follow at the end. It is not necessary for understanding now.

With the @RequestMapping annotation on the method, we define the endpoint of the remote call and what HTTP method we will use. In this case a GET to /flight/FRA/departure.

Now, we can inject the FlightClient into our code like any other Spring Bean. On startup, Spring Cloud will set up the Feign client for us and give us a regular Spring Proxy so we can simply start working with the remote end.

Path variables and query params are also supported by the client like:

@RequestMapping(method = RequestMethod.GET, value = "/flightDetails/{airlineCode}/{flightNumber}/")
List<FlightWrapper> getFlightDetails(
                @PathVariable("airlineCode") String airlineCode, 
                @PathVariable("flightNumber") Integer flightNumber
);

It is the same as with MVC on the server side.

Authentication

The FRAPort API requires authentication. Like Spring MVC, Feign has an interceptor concept, which can be used to do certain stuff before a remote call. The entry is the RequestInterceptor interface.

With Spring we just need to provide a Bean implementing that particular interface to the Spring context and it will be automatically picked up.

@Bean
AuthInterceptor authFeign() {
    return new AuthInterceptor();
}

class AuthInterceptor implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate template) {
        template.header("Authorization", "Bearer <yours token>");

    }

}

In our case, we just set the Authorization to our hard-coded token. You need to insert yours here.

Now, let’s put everything together and the app will look like:

@SpringBootApplication
@EnableFeignClients
public class FeignTutorialFraportApplication implements CommandLineRunner {

    @Autowired
    private FlightClient flightClient;

    @Bean
    AuthInterceptor authFeign() {
        return new AuthInterceptor();
    }

    class AuthInterceptor implements RequestInterceptor {

        @Override
        public void apply(RequestTemplate template) {
            template.header("Authorization", "Bearer <your token>");

        }

    }

    public static void main(String[] args) {
        new SpringApplicationBuilder(RestTemplateTutorialFraportApplication.class)
            .web(WebApplicationType.NONE)
            .run(args);
    }

    @Override
    public void run(String... args) throws Exception {

        List<String> dest = Arrays.asList(args);

        for (FlightWrapper flightWrapper : flightClient.getDepartingFlights()) {
            Flight flight = flightWrapper.getFlight();

            if (dest.contains(flight.getArrivalAirport())) {
                System.out.println(
                  String.format(
                      "%s\tto\t%s\ton\t%s\twith\t%s", 
                      flight.getDepartureAirport(),
                    flight.getArrivalAirport(), 
                    flight.getDeparture().getScheduled(), 
                    flight.getOperatingAirline().getName()
                  )
                );
            }
        }
    }
}

Now, when running the app, we provide a destination airports code and it will output all flights in the next 24h to that destination. Use HND (Tokyo Haneda, Japan) for example.

In case you might wonder, no, the API does not offer queries against specific destinations only.

The POJO Boilerplate & Source code

The POJO follow the schema of the API and include some wrappers. As there is nothing special about them, I do not include them here, but you can get the full working example on GitHub.


Want content like this in your inbox each workday? No BS, spam or tricks... just useful content:

I understand and agree to the privacy policy