spring-framework icon indicating copy to clipboard operation
spring-framework copied to clipboard

Add Support to CompletableFuture as return type in Spring interfaces clients with RestClient

Open ne3ma-ibraheem opened this issue 7 months ago • 0 comments

I suggest adding support for CompletableFuture and CompletionStage as a valid return type for methods in Spring client interfaces backed by RestClient and RestClientAdapter.

It would be great to provide the ExecutorService when creating the adapter or maybe leverage Async annotation

Example

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestClient;
import org.springframework.web.service.annotation.GetExchange;
import org.springframework.web.service.annotation.PostExchange;
import org.springframework.web.service.annotation.RequestBody;
import org.springframework.web.service.annotation.PathVariable;
import org.springframework.http.ResponseEntity;
import org.springframework.web.service.invoker.HttpServiceProxyFactory;

public interface MyApiClient {

    @GetExchange("/users/{id}")
    CompletableFuture<User> getUser(@PathVariable int id);

    @PostExchange("/users")
    CompletableFuture<ResponseEntity<User>> createUser(@RequestBody User user);

    @GetExchange("/items")
    CompletableFuture<List<Item>> getAllItems();
}

@Configuration
public class ClientConfig {

    @Bean
    public MyApiClient myApiClient(RestClient.Builder restClientBuilder) {
        RestClient restClient = restClientBuilder.baseUrl("https://api.example.com").build();
        HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(RestClientAdapter.forClient(restClient)).build();
        return factory.createClient(MyApiClient.class);
    }
}

@Service
public class MyService {

    private final MyApiClient apiClient;

    public MyService(MyApiClient apiClient) {
        this.apiClient = apiClient;
    }

    public void processUser(int userId) {
        apiClient.getUser(userId)
                 .thenAccept(user -> System.out.println("Retrieved user: " + user))
                 .exceptionally(ex -> {
                     System.err.println("Error fetching user: " + ex.getMessage());
                     return null;
                 });
    }
}

ne3ma-ibraheem avatar Apr 13 '25 06:04 ne3ma-ibraheem