Generating Feign clients with Swagger Codegen and Gradle

In the last article, I showed you how to use a custom template for code generation. If you haven’t read the previous parts of the series, make sure you do that before continuing.

About Feign

First of all, I won’t give you a deep intro into Feign as the article is not about that, but rather I’ll give a high-level overview. Feign’s goal is to enable building declarative HTTP clients with lots of customization. Imagine that you want to make an HTTP call from ServiceA to ServiceB. If you are familiar with the Spring world, most probably you’d go with Spring’s RestTemplate . There’s nothing wrong with that, but Feign approaches this problem a bit differently. You just define an interface in ServiceA according to the contract of ServiceB and make some configuration like where the endpoint lies, what are the parameters, headers, etc.

Let’s have a quick example. This can also be found on OpenFeign’s GitHub page.

interface GitHub {
  @RequestLine("GET /repos/{owner}/{repo}/contributors")
  List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);
}

static class Contributor {
  String login;
  int contributions;
}

public static void main(String... args) {
  GitHub github = Feign.builder()
                       .decoder(new GsonDecoder())
                       .target(GitHub.class, "https://api.github.com");

  // Fetch and print a list of the contributors to this library.
  List<Contributor> contributors = github.contributors("OpenFeign", "feign");
  for (Contributor contributor : contributors) {
    System.out.println(contributor.login + " (" + contributor.contributions + ")");
  }
}

As you can see, an interface is defined for the API, called GitHub and you can create a client according to that using Feign’s builder API. After you’re done with building, you can invoke the GitHub API via a simple method call.

Additionally, Feign is capable of multiple customization like using Ribbon, Hystrix, special logging or request management and many more.

Feign with Spring Cloud

Feign has it’s custom annotations for defining the APIs and you have to build your clients manually. Spring Cloud has a Feign integration which means that you can have Spring MVC annotations to define the API, like @RequestMapping , @PathVariable , @RequestParam , etc. Also, you don’t need to manually create the clients, you can just annotate the interface with @FeignClient  and add the proper configuration there plus put the @EnableFeignClients  annotation on one of the configuration classes.

This integration with Spring’s ecosystem comes handy as you can use the Feign clients as regular beans. Inject them into a bean and just simply invoke the method you want. Very very useful and clean compared to a RestTemplate  for example.

Generating Feign clients

Now back to Swagger code generation. It’s already a great thing if you can provide a contract for your API for clients but even better if you could prepare an artifact with which the clients can interact with your API. With Spring Cloud, it’s possible to have Feign clients which doesn’t have hardcoded URLs, ports for the services they are working with instead they can use Eureka to resolve the services by name. This is what we’ll use for generating the Feign clients.

Swagger Codegen has 3 libraries for the language spring : spring-boot , spring-cloud , spring-mvc . For generating Feign clients, we’ll use spring-cloud as it’s already prepared for it, we’ll just customize it a bit for Eureka discovery.

Starting from the state where the previous article ended, we have the template  folder in the project which contains the mustache templates for generation. In the libraries  folder, there are 3 sub-directories, each for one of the 3 libraries. As we are going to use the spring-cloud library , let’s remove spring-boot  and spring-mvc .

Okay, now let’s extend the buildscript with generating the Feign client. First of all, we’ll need a new placeholder project next to the other one, call it user-service-feign-client . The updated settings.gradle  looks the following:

def name = 'user-service'
def rootName = "${name}-contract"
def serverName = "${name}-server"
def clientName = "${name}-feign-client"

rootProject.name = rootName

file(serverName).mkdir()
file(clientName).mkdir()

include serverName
include clientName

Including the Feign client generation in the build process is not that complicated if we’re reusing the existing server-side generator. The build.gradle  is extended with the following

project("${rootProject.appName}-feign-client") { // user-service-feign-client
    // Dependencies for the generated sources
    dependencies {
        compile('org.springframework.boot:spring-boot-starter-web:1.5.13.RELEASE')
        compile('org.springframework.cloud:spring-cloud-starter-feign:1.4.0.RELEASE')
        compile('io.springfox:springfox-swagger2:2.7.0')
    }

    // Actual task for generating the Feign client
    task generateClient {
        doLast {
            def config = new CodegenConfigurator()
            config.setLang("spring")
            config.setLibrary('spring-cloud')
            config.setApiPackage(rootProject.apiPackage)            // Package to be used for the API interfaces
            config.setModelPackage(rootProject.modelPackage)        // Package to be used for the API models
            config.setInputSpec(rootProject.swaggerFile.toString()) // The swagger API file
            config.setOutputDir(project.buildDir.toString())        // The output directory, user-service-contract/build/user-service-feign-client/
            config.setTemplateDir(rootProject.templateDir)          // The directory where the templates are located
            config.setAdditionalProperties([
                    'dateLibrary'  : 'java8',
                    'title'        :  rootProject.appName,
                    'useTags'      : 'true'
            ])
            new DefaultGenerator().opts(config.toClientOptInput()).generate()
        }
    }
    compileJava.dependsOn('generateClient')
}

Again, referencing the client project, adding the dependencies which will be used for the client code and setting up the Swagger generator. The important change here is that there is a new setting where we set the used library which is spring-cloud . Also, there is an additional property called title  which will be used in the client template for Eureka discovery.

After executing ./gradlew clean build , there will be an error popping up which describes that there is a compilation failure due to missing classes.

error: package org.springframework.cloud.security.oauth2.client.feign does not exist
import org.springframework.cloud.security.oauth2.client.feign.OAuth2FeignRequestInterceptor;
                                                             ^
error: package org.springframework.security.oauth2.client does not exist
import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext;
                                                 ^
...

This happens because Swagger’s default template is using OAuth2 for the client generation along with other custom interceptors. Usually I don’t need this so I’ll show you a way how to get rid of this.

Change the client generation in the build.gradle  to this:

project("${rootProject.appName}-feign-client") { // user-service-feign-client
    ...
    task generateClient {
        ...
    }

    // Task for deleting the unnecessary configuration
    task deleteNonClientRelatedClasses(dependsOn: 'generateClient') {
        doLast {
            delete "${project.buildDir}/src/main/java/io"
        }
    }
    compileJava.dependsOn('deleteNonClientRelatedClasses')
}

There is a new task added, called deleteNonClientRelatedClasses  which is responsible to delete the src/main/java/io  folder where Swagger Codegen puts the configuration classes by default. Now we are left with a single error that the ClientConfiguration  class is not found for the generated Feign clients, but this is expected as we deleted it.

error: cannot find symbol
@FeignClient(name="${user-service.name:user-service}", url="${user-service.url:https://localhost}", configuration = ClientConfiguration.class)
                                                                                                                    ^
symbol: class ClientConfiguration

The Feign client template is stored in the apiClient.mustache  file within the spring-cloud  library folder. Let’s open it and delete the unnecessary configuration for the client. The default template is the following:

package {{package}};

import org.springframework.cloud.netflix.feign.FeignClient;
import {{configPackage}}.ClientConfiguration;

{{=<% %>=}}
@FeignClient(name="${<%title%>.name:<%title%>}", url="${<%title%>.url:<%basePath%>}", configuration = ClientConfiguration.class)
<%={{ }}=%>
public interface {{classname}}Client extends {{classname}} {
}

Now we can remove the import for the ClientConfiguration  class as well as configuration for the @FeignClient  annotation. Also, as only Eureka discovery will be used for the Feign clients, I’ll simply remove the url  configuration as well and end up with this:

package {{package}};

import org.springframework.cloud.netflix.feign.FeignClient;

@FeignClient("{{title}}")
public interface {{classname}}Client extends {{classname}} {
}

It’s a really basic Feign client and the service will be resolved by it’s name, in this case the name of the service should be user-service  in Eureka. This is how the generated code looks like:

package com.arnoldgalovics.blog.userservice.api;

import org.springframework.cloud.netflix.feign.FeignClient;

@FeignClient("user-service")
public interface UserApiClient extends UserApi {
}

After executing again ./gradlew clean build install , this client artifact can be included in any other service to invoke the user-service .

Testing time

Testing it is a bit tricky as we’ll need a Eureka server, we have to enhance the current user-service to register itself to Eureka and create actually another service which will call the user-service via Feign.

Let’s start with adding Eureka support to the user-service. The build.gradle  for the service will now include spring-cloud-eureka  as a dependency and the Application  class will have @EnableDiscoveryClient  annotation on it.

ext {
    springCloudVersion = 'Edgware.SR3'
}

dependencies {
    compile('org.springframework.boot:spring-boot-starter-web')
    compile('org.springframework.cloud:spring-cloud-starter-eureka')
    compile('com.arnoldgalovics.blog:user-service-server:0.0.1-SNAPSHOT')
}

dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
    }
}
@SpringBootApplication
@EnableDiscoveryClient
public class UserServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserServiceApplication.class, args);
    }
}

Okay, now check out the Eureka service. It’s basically a simple Spring Boot app with a dependency to eureka-starter  and a bit of configuration. I’ve created a new project called discovery-service with Spring Initializr. The most important thing here is the @EnableEurekaServer  annotation on the Application class and the following bootstrap.yml .

server:
  port: 8761

spring:
  application:
    name: discovery-service

eureka:
  client:
    fetch-registry: false
    register-with-eureka: false

Next up, we’ll need another service where we can trigger the Feign invocation. Imagine a payment-service which will interact with the user-service. It will be a simple Spring Boot app with Eureka client and a very simple controller.

After creating the project, open the build.gradle  and include the generated Feign client. Of course, don’t forget to add mavenLocal()  as a repository.

dependencies {
    compile('com.arnoldgalovics.blog:user-service-feign-client:0.0.1-SNAPSHOT')
    ...
}

The Application  class looks the following:

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients(basePackageClasses = UserApiClient.class)
public class PaymentServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(PaymentServiceApplication.class, args);
    }
}

Notice the @EnableFeignClients  annotation which will initiate the Feign client building process. Create a PaymentController  with one method available which will invoke the client.

@RestController("/payment")
class PaymentController {
    private UserApiClient userApiClient;

    public PaymentController(UserApiClient userApiClient) {
        this.userApiClient = userApiClient;
    }

    @GetMapping
    public ListPayingUserResponse getPayingUsers() {
        return new ListPayingUserResponse(userApiClient.getUsers().getBody().stream().map(UserResponse::getName).collect(Collectors.toList()));
    }
}
public class ListPayingUserResponse {
    private List<String> names;

    public ListPayingUserResponse(List<String> names) {
        this.names = names;
    }

    public List<String> getNames() {
        return names;
    }

    public void setNames(List<String> names) {
        this.names = names;
    }
}

One last step is missing, to fill up the response from user-service. Go back to the delegate and add some static data to the getUsers  method.

@Component
public class UserApiDelegateImpl implements UserApiDelegate {
    @Override
    public ResponseEntity<Void> createUser(UserCreateRequest user) {
        // TODO: implementation
        return null;
    }

    @Override
    public ResponseEntity<ListUserResponse> getUsers() {
        ListUserResponse response = new ListUserResponse();
        response.add(new UserResponse().name("Arnold").id(1));
        return ResponseEntity.ok(response);
    }
}

Start the discovery-service first, then the payment-service and the user-service. You can do this from your favorite IDE or by executing ./gradlew clean bootRun  on each of them.

Open up your Postman or any other REST Client and invoke GET http://localhost:8001/payment . If you did everything correctly, you’ll see the following response coming back:

{
    "names": [
        "Arnold"
    ]
}

Cool, right? We just used the generated client from the contract to handle interaction between services.

Recap

In this article, we’ve checked how you can generate Feign clients from a Swagger contract. It’s extremely useful when you want to provide not only your API contract, but a ready to be used artifact with which you can invoke the actual API. Also, we’ve created a small test environment to test the generated Feign client and invoke the service which has the Swagger generated API.

The full project is available on GitHub.

Let me know what you think about this part of the series in the comments or on Twitter. Share it, like it, more articles are coming.

Leave a Reply

Your email address will not be published.