Search code examples
feign

Feign - define param value for each methods


I need to write a client with multiple methods that require the apiKey as query string param. Is it possible to allow the client's user to pass the api key only to the method withApiKey, so I can avoid to request the apiKey as first parameter of each method?

public interface Client {
    @RequestLine("GET /search/search?key={apiKey}&query={query}&limit={limit}&offset={offset}")
    SearchResponse search(@Param("apiKey") String apiKey, @Param("query") String query, @Param("limit") Integer limit, @Param("offset") Integer offset);

    @RequestLine("GET /product/attributes?key={apiKey}&products={products}")
    List<Product> getProduct(@Param("apiKey") String apiKey, @Param("products") String products);

    public class Builder {
        private String basePath;
        private String apiKey;

        public Client build() {
            return Feign.builder()
                    .encoder(new JacksonEncoder())
                    .decoder(new JacksonDecoder())
                    .client(new ApacheHttpClient())
                    .logger(new Slf4jLogger())
                    .logLevel(Logger.Level.FULL)
                    .target(Client.class, basePath);

        }

        public Builder withBasePath(String basePath) {
            this.basePath = basePath;
            return this;
        }

        public Builder withApiKey(String apiKey) {
            this.apiKey = apiKey;
            return this;
        }
    }
}

Solution

  • Depending on the setup request-interceptors might work: https://github.com/OpenFeign/feign#request-interceptors

    Hopefully the example below will help.

    You can swap the builder out for just the interface annotation and then move the configuration to a configuration class, if you are using spring it could be like:

    @FeignClient(
        name = "ClerkClient",
        url = "${clery-client.url}", // instead of the withBasePath method 
        configuration = {ClerkClientConfiguration.class}
    )
    public interface Client {
    

    Then the ClerkClientConfiguration class can define the required config beans including a ClerkClientInterceptor

    public class ClerkClientConfiguration {
    
    @Bean
    public RequestInterceptor clerkClientInterceptor() {
        return new ClerkClientInterceptor();
    }
    

    Then the interceptor can have a value picked up from the config and added to the queries (or header etc)

        public class ClerkClientInterceptor implements RequestInterceptor {
        
        @Value("${clerk-client.key}")
        private String apiKey
    
        @Override public void apply(RequestTemplate template) {
           requestTemplate.query( "key", apiKey); 
        }