CoApi ClientMode Configuration: Choosing Between REACTIVE, SYNC, and AUTO

CoApi's ClientMode enum determines whether the framework instantiates non-blocking WebClient beans (REACTIVE), blocking RestClient beans (SYNC), or automatically infers the correct implementation (AUTO) by inspecting the classpath for Spring WebFlux classes.

The ahoo-wang/coapi library abstracts HTTP client creation through the ClientMode configuration setting defined in ClientMode.kt. This enum controls the underlying Spring HTTP client architecture, selecting between reactive and synchronous execution models. Choosing the correct mode ensures your API clients align with your application's threading model and performance requirements.

How CoApi Uses ClientMode

CoApi delegates client instantiation to CoApiRegistrar, which examines the configured mode during Spring context initialization. In /spring/src/main/kotlin/me/ahoo/coapi/spring/CoApiRegistrar.kt, the register() method branches based on the ClientMode value to register either a WebClientFactoryBean or RestClientFactoryBean.

REACTIVE Mode

When ClientMode.REACTIVE is specified, CoApi registers WebClientFactoryBean instances for all client interfaces. This creates non-blocking WebClient implementations suitable for Spring WebFlux applications.

Use REACTIVE mode when:

  • Building applications on the Spring WebFlux reactive stack
  • Requiring non-blocking I/O for high concurrency scenarios
  • Working with Mono and Flux return types

The registration occurs in CoApiRegistrar.register() at /spring/src/main/kotlin/me/ahoo/coapi/spring/CoApiRegistrar.kt, specifically targeting the WebClient creation path.

SYNC Mode

Setting ClientMode.SYNC forces CoApi to register RestClientFactoryBean beans, producing blocking RestClient implementations. This aligns with traditional Spring MVC servlet-based architectures.

Use SYNC mode when:

  • Working with Spring MVC or servlet-based applications
  • Calling legacy services from blocking codebases
  • Preferring imperative programming models with direct return values

The synchronous path is handled in the same register() method, branching when clientMode == ClientMode.SYNC.

AUTO Mode

AUTO is the default behavior. CoApi invokes ClientMode.inferClientMode() (defined in /spring/src/main/kotlin/me/ahoo/coapi/spring/ClientMode.kt) to detect the web stack automatically:

private val INFERRED_MODE_BASED_ON_CLASS = try {
    Class.forName("org.springframework.web.reactive.HandlerResult")
    REACTIVE
} catch (e: ClassNotFoundException) {
    SYNC
}

This logic checks for the presence of org.springframework.web.reactive.HandlerResult. If found, CoApi assumes a WebFlux environment and selects REACTIVE; otherwise, it defaults to SYNC.

Use AUTO mode when:

  • You want zero-configuration setup
  • Your project clearly uses either WebFlux or MVC (but not both)
  • You want CoApi to "just work" based on classpath inspection

You can override AUTO detection using the coapi.mode property defined in CoApiProperties.kt.

Practical Configuration Examples

Forcing REACTIVE Mode


# application.yml

coapi:
  mode: REACTIVE
@Service
public class GitHubService {
    private final GitHubApi gitHubApi;
    
    // Injected WebClient-based implementation
    public GitHubService(GitHubApi gitHubApi) {
        this.gitHubApi = gitHubApi;
    }
}

Forcing SYNC Mode


# application.yml

coapi:
  mode: SYNC
@Service
public class IssueService {
    private final IssueApi issueApi;
    
    // Injected RestClient-based implementation (blocking)
    public IssueService(IssueApi issueApi) {
        this.issueApi = issueApi;
    }
}

Using AUTO (Default)


# application.yml

coapi:
  # mode omitted - defaults to AUTO

No additional configuration required. CoApi inspects the classpath during startup via AbstractCoApiRegistrar and selects the appropriate factory.

Summary

  • REACTIVE mode instantiates WebClientFactoryBean for non-blocking WebFlux applications, registered through CoApiRegistrar in /spring/src/main/kotlin/me/ahoo/coapi/spring/CoApiRegistrar.kt
  • SYNC mode creates RestClientFactoryBean for blocking MVC applications using the same registration logic
  • AUTO mode executes inferClientMode() in ClientMode.kt to detect WebFlux via Class.forName("org.springframework.web.reactive.HandlerResult"), defaulting to SYNC if absent
  • Override automatic detection using the coapi.mode property constant defined in CoApiProperties.kt

Frequently Asked Questions

How does AUTO mode detect which client type to use?

AUTO mode calls ClientMode.inferClientMode() which attempts to load org.springframework.web.reactive.HandlerResult via reflection. If the class exists, CoApi assumes a WebFlux environment and selects REACTIVE; if a ClassNotFoundException occurs, it defaults to SYNC. This logic resides in /spring/src/main/kotlin/me/ahoo/coapi/spring/ClientMode.kt.

Can I use REACTIVE mode in a Spring MVC application?

While technically possible, using REACTIVE mode in a Spring MVC application creates a mismatch between the blocking servlet threads and the non-blocking WebClient. This can lead to thread starvation or complex interoperability issues. CoApi allows explicit configuration via coapi.mode=REACTIVE, but you should ensure your calling code handles reactive types appropriately.

What property overrides the AUTO detection?

The coapi.mode property (constant COAPI_CLIENT_MODE_PROPERTY in CoApiProperties.kt) accepts values of REACTIVE, SYNC, or AUTO. Set this in application.yml or as a JVM system property to bypass automatic inference.

Where does CoApi register the actual client beans?

The CoApiRegistrar.register() method in /spring/src/main/kotlin/me/ahoo/coapi/spring/CoApiRegistrar.kt performs the registration. It checks the ClientMode and instantiates either WebClientFactoryBean (REACTIVE) or RestClientFactoryBean (SYNC), both defined in their respective subpackages under /spring/src/main/kotlin/me/ahoo/coapi/spring/client/.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →