# WebClient vs RestClient Backends in CoApi: A Complete Technical Comparison

> Compare WebClient and RestClient backends in CoApi. Learn the key differences: WebClient offers non-blocking reactive calls, RestClient provides synchronous blocking requests. Understand CoApi's HTTP client configurations.

- Repository: [Ahoo Wang/coapi](https://github.com/ahoo-wang/coapi)
- Tags: comparison
- Published: 2026-02-23

---

**WebClient provides non-blocking reactive HTTP calls returning Mono or Flux, while RestClient offers synchronous blocking requests returning plain objects, with both backends sharing common configuration patterns through AbstractHttpClientFactoryBean in the CoApi framework.**

The `ahoo-wang/coapi` library abstracts HTTP client creation for Spring applications, offering developers a choice between reactive and synchronous execution models. When defining CoApi interfaces, selecting between **WebClient and RestClient backends** determines your application's concurrency characteristics, return types, and load-balancing mechanisms. Understanding these distinctions ensures optimal performance for both high-throughput reactive services and traditional blocking architectures.

## Core Architecture and Factory Beans

### WebClient Factory Structure

In [`spring/src/main/kotlin/me/ahoo/coapi/spring/client/reactive/WebClientFactoryBean.kt`](https://github.com/ahoo-wang/coapi/blob/main/spring/src/main/kotlin/me/ahoo/coapi/spring/client/reactive/WebClientFactoryBean.kt), CoApi creates `org.springframework.web.reactive.function.client.WebClient` instances for reactive workloads. This factory extends [`AbstractWebClientFactoryBean.kt`](https://github.com/ahoo-wang/coapi/blob/main/AbstractWebClientFactoryBean.kt), which manages base URL resolution from `CoApiDefinition` and applies `WebClientBuilderCustomizer` configurations.

The factory automatically detects load-balancing requirements and, when necessary, utilizes `LoadBalancedWebClientBuilderCustomizer` to inject `LoadBalancedExchangeFilterFunction` into the builder chain.

### RestClient Factory Structure

Conversely, [`spring/src/main/kotlin/me/ahoo/coapi/spring/client/sync/RestClientFactoryBean.kt`](https://github.com/ahoo-wang/coapi/blob/main/spring/src/main/kotlin/me/ahoo/coapi/spring/client/sync/RestClientFactoryBean.kt) instantiates `org.springframework.web.client.RestClient` for blocking operations, extending [`AbstractRestClientFactoryBean.kt`](https://github.com/ahoo-wang/coapi/blob/main/AbstractRestClientFactoryBean.kt). Both factories ultimately inherit from `AbstractHttpClientFactoryBean`, ensuring consistent property handling while maintaining separate customization hooks for their respective Spring client types.

When load balancing is enabled, this factory employs `LoadBalancedRestClientBuilderCustomizer` to configure `LoadBalancerInterceptor` appropriately.

## Execution Models and Return Types

The fundamental distinction lies in the response handling and threading models:

**WebClient (Reactive)**: Returns **Mono<T>** or **Flux<T>** types, enabling non-blocking I/O with back-pressure support. The underlying Netty event loop handles concurrent requests without blocking threads, making it ideal for high-throughput scenarios.

**RestClient (Synchronous)**: Returns plain objects (e.g., `String`, `List<T>`, or custom DTOs). Each request blocks the calling thread until the HTTP response completes, simplifying imperative programming models but consuming thread resources per request.

## Customization Hooks and Load Balancing

Both backends support Spring Cloud load balancing but implement cross-cutting concerns through different interceptor patterns:

**WebClient Customization**:
- Uses `WebClientBuilderCustomizer` to inject `ExchangeFilterFunction` instances
- Load balancing leverages `LoadBalancedExchangeFilterFunction` (reactive filter)
- Configuration via `ClientProperties.filter` list processed in [`AbstractWebClientFactoryBean.kt`](https://github.com/ahoo-wang/coapi/blob/main/AbstractWebClientFactoryBean.kt)

**RestClient Customization**:
- Uses `RestClientBuilderCustomizer` to inject `ClientHttpRequestInterceptor` instances
- Load balancing leverages `LoadBalancerInterceptor` (blocking interceptor)
- Configuration via `ClientProperties.interceptor` list processed in [`AbstractRestClientFactoryBean.kt`](https://github.com/ahoo-wang/coapi/blob/main/AbstractRestClientFactoryBean.kt)

When `loadBalanced()` is enabled or a `serviceId` is specified, the respective factory automatically selects the appropriate load-balancing customizer before applying any globally registered customizer beans in order.

## Practical Configuration Examples

### Reactive API with WebClient

Define an interface returning reactive types to leverage the WebClient backend:

```kotlin
@CoApi(baseUrl = "https://api.github.com")
interface GitHubReactiveApi {
    @GetExchange("repos/{owner}/{repo}/issues")
    fun listIssues(@PathVariable owner: String,
                   @PathVariable repo: String): Flux<Issue>
}

```

The method returns a `Flux<Issue>`—the call executes on the non-blocking Netty event loop.

### Synchronous API with RestClient

Define an interface returning concrete types to use the RestClient backend:

```kotlin
@CoApi(baseUrl = "https://api.github.com")
interface GitHubSyncApi {
    @GetExchange("repos/{owner}/{repo}/issues")
    fun listIssues(@PathVariable owner: String,
                   @PathVariable repo: String): List<Issue>
}

```

The method returns a plain `List<Issue>`—the `RestClient` performs a blocking HTTP request.

### Enabling Load Balancing

Both backends support identical service discovery syntax:

```kotlin
@CoApi(serviceId = "github-service")
interface LoadBalancedApi {
    @GetExchange("/endpoint")
    fun fetchData(): String
}

```

The factories automatically detect load-balancing requirements and inject `LoadBalancedExchangeFilterFunction` for WebClient or `LoadBalancerInterceptor` for RestClient accordingly.

## Summary

- **WebClient backends** in CoApi provide non-blocking reactive HTTP via `WebClientFactoryBean`, returning Mono/Flux and using `ExchangeFilterFunction` for cross-cutting concerns.
- **RestClient backends** deliver synchronous blocking HTTP via `RestClientFactoryBean`, returning plain objects and using `ClientHttpRequestInterceptor` for request modification.
- Both architectures share common base configuration through `AbstractHttpClientFactoryBean` while maintaining distinct customization patterns for filters versus interceptors.
- Load balancing works identically from the consumer perspective but uses reactive filters for WebClient and blocking interceptors for RestClient under the hood.

## Frequently Asked Questions

### Can I use both WebClient and RestClient backends in the same CoApi application?

Yes. CoApi supports mixing reactive and synchronous clients within the same Spring application. Define separate interfaces annotated with `@CoApi`—returning `Mono` or `Flux` for WebClient usage and plain types for RestClient. The framework instantiates the appropriate factory bean based on your method return types and classpath dependencies.

### How does load balancing configuration differ between the two backends?

From the developer perspective, configuration is identical: use `serviceId` or `lb://` URLs in your `@CoApi` annotation. Internally, `WebClientFactoryBean` injects `LoadBalancedExchangeFilterFunction` (a reactive filter), while `RestClientFactoryBean` injects `LoadBalancerInterceptor` (a blocking interceptor). Both are configured automatically when load balancing is detected.

### Which backend should I choose for high-concurrency microservices?

Choose **WebClient** for high-concurrency scenarios requiring efficient resource utilization. Its non-blocking nature allows handling thousands of concurrent connections with fewer threads, leveraging back-pressure through `Flux` and `Mono` return types. **RestClient** suits simpler workloads or when integrating with legacy blocking code where thread-per-request semantics are acceptable.

### Where are the factory implementations located in the CoApi source code?

The reactive stack resides in `spring/src/main/kotlin/me/ahoo/coapi/spring/client/reactive/`, containing [`WebClientFactoryBean.kt`](https://github.com/ahoo-wang/coapi/blob/main/WebClientFactoryBean.kt) and [`AbstractWebClientFactoryBean.kt`](https://github.com/ahoo-wang/coapi/blob/main/AbstractWebClientFactoryBean.kt). The synchronous stack lives in `spring/src/main/kotlin/me/ahoo/coapi/spring/client/sync/`, containing [`RestClientFactoryBean.kt`](https://github.com/ahoo-wang/coapi/blob/main/RestClientFactoryBean.kt) and [`AbstractRestClientFactoryBean.kt`](https://github.com/ahoo-wang/coapi/blob/main/AbstractRestClientFactoryBean.kt).