Declarative REST client

Spring Framework 6

Posted by Xavier Bouclet on February 04, 2024 · 4 mins read

Declarative REST client

1. Purpose of this blog post

To create a REST client easily with Spring, we often use feign. For Spring Boot, we can use :

  • org.springframework.cloud:spring-cloud-starter-openfeign for webmvc

  • com.playtika.reactivefeign:feign-reactor-spring-cloud-starter for webflux

There is no unified way of creating a feign client, at least prior to Spring Framework 6.

Now, with Spring Framework 6 (or Spring Boot 3), we can leverage the declarative REST client.

TODO ⇒ code

2. Creating a declarative REST client

Let’s use the Spring Initialzr to create the Spring Boot project.

  • Group : com.xavierbouclet

  • Artifact : whiskies-api

  • Project : Maven

  • Language : Java

  • Java : 17

  • Dependencies :

    • Spring Web

    • Spring Reactive Web

    • Validation

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.xavierbouclet</groupId>
    <artifactId>whiskies-api</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>whiskies-api</name>
    <description>whiskies-api</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

Let’s create an interface for our REST client.

public interface WhiskyService {

    @GetExchange("/whiskies")
    List<Whisky> loadAll();
}
client:
  whisky:
    service:
      url: https://my-json-server.typicode.com/mikrethor/whiskies
import jakarta.validation.constraints.NotNull;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;

@Validated
@ConfigurationProperties(prefix = "client.whisky.service")
public record WhiskyClientProperties(@NotNull String url) {
}
    @Bean
    WebClient webClient(WhiskyClientProperties whiskyClientProperties) {
        return  WebClient.builder().baseUrl(whiskyClientProperties.url())
                .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).build();
    }

    @Bean
    HttpServiceProxyFactory proxyFactory(WebClient client) {
        return HttpServiceProxyFactory.builder(WebClientAdapter.forClient(client)).build();
    }

    @Bean
    WhiskyService whiskyService(HttpServiceProxyFactory factory) {
        return factory.createClient(WhiskyService.class);
    }

3. Adapt the client for Webflux

public interface WhiskyService {

    @GetExchange("/whiskies")
    Flux<Whisky> loadAll();
}

4. Conclusion

In my point of view, Declarative REST client is a nicer way to declare REST client than Feign.

If you want to check the final code on GitHub.

Follow Me