How CoApi Interceptors Work: Modify Requests and Responses in Spring Boot

CoApi interceptors let you modify HTTP requests and responses by implementing Spring's ClientHttpRequestInterceptor interface and registering your beans via YAML configuration, with the framework automatically wiring them into the synchronous RestClient built for each CoApi client.

CoApi interceptors provide a simple, extensible mechanism to plug custom request/response processing into your Spring-based API clients. In the ahoo-wang/coapi repository, this mechanism leverages Spring's RestClient builder to seamlessly integrate standard ClientHttpRequestInterceptor implementations. Whether you need to add authentication headers, log traffic, or transform payloads, interceptors give you fine-grained control over the HTTP lifecycle.

Understanding the CoApi Interceptor Architecture

Configuration Model

The interceptor configuration is defined in ClientProperties.InterceptorDefinition, which maintains two distinct lists for registration. The names list contains bean names of interceptors already registered in the Spring context, while the types list holds concrete classes that Spring can instantiate directly.

You access these definitions through the ClientProperties#getInterceptor(coApiName) method, located in /spring/src/main/kotlin/me/ahoo/coapi/spring/client/ClientProperties.kt at lines 31-34. This method retrieves the specific interceptor configuration bound to your named CoApi client.

Binding to the RestClient Builder

When CoApi constructs a synchronous client, AbstractRestClientFactoryBean retrieves the interceptor definition for the current CoApi (definition.name) and registers the interceptors with the RestClient builder. This happens at lines 37-41 of /spring/src/main/kotlin/me/ahoo/coapi/spring/client/sync/AbstractRestClientFactoryBean.kt:

val interceptorDefinition = clientProperties.getInterceptor(definition.name)
clientBuilder.requestInterceptors {
    interceptorDefinition.initInterceptors(it)
}

Initialization and Execution Order

The extension function ClientProperties.InterceptorDefinition.initInterceptors, found at lines 50-60 of AbstractRestClientFactoryBean.kt, resolves each bean name or class from the application context and adds the resulting ClientHttpRequestInterceptor to the mutable list supplied by the builder.

Interceptors execute in the order they appear in configuration: first all entries in the names list, followed by all entries in the types list. The RestClient invokes them sequentially, allowing each interceptor to inspect or modify the outgoing HttpRequest and the incoming ClientHttpResponse.

Implementing a Custom CoApi Interceptor

Because interceptors implement Spring's standard ClientHttpRequestInterceptor interface, you can modify request headers, log payloads, apply authentication, or rewrite response bodies. Here is a complete example that adds a custom header to every request:

@Component
class HeaderAddingInterceptor : ClientHttpRequestInterceptor {
    override fun intercept(
        request: HttpRequest,
        body: ByteArray,
        execution: ClientHttpRequestExecution
    ): ClientHttpResponse {
        val modified = request.headers.apply {
            add("X-Custom-Header", "value")
        }
        return execution.execute(request, body)
    }
}

Configuring Interceptors for Your CoApi Client

Register your interceptor via application.yml using the coapi.clients.{clientName}.sync.interceptor namespace. You can reference beans by name or by fully-qualified class name:

coapi:
  clients:
    GitHubApiClient:
      sync:
        interceptor:
          names: [ headerAddingInterceptor ]   # bean name (lower‑camel case)

          # or alternatively:

          # types: [ com.example.HeaderAddingInterceptor ]

The configuration binding logic resides in /spring-boot-starter/src/main/kotlin/me/ahoo/coapi/spring/boot/starter/CoApiProperties.kt at lines 48-64, with validation tests available in CoApiPropertiesTest.kt at lines 112-114.

Request Lifecycle and Execution Flow

When you invoke a method on your CoApi client, the request flows through the interceptor chain before reaching the target API:


GitHubService -> RestClient (built by AbstractRestClientFactoryBean) 
    → requestInterceptors (HeaderAddingInterceptor)
    → HTTP request sent to GitHub API

Any request sent through GitHubApiClient will now contain the "X-Custom-Header" injected by your interceptor.

Advanced Configuration Patterns

Because interceptors are standard Spring beans, you can leverage the full Spring ecosystem for advanced use cases. Use @ConditionalOnProperty or profile-based configuration to conditionally register interceptors for specific environments. You can also replace implementations at runtime by overriding bean definitions in test configurations or using @Primary annotations.

@Service
class GitHubService(val gitHubApiClient: GitHubApiClient) {
    fun listRepos() = gitHubApiClient.repos().list()
}

Summary

  • CoApi interceptors implement Spring's ClientHttpRequestInterceptor interface for seamless integration with RestClient.
  • Configuration uses ClientProperties.InterceptorDefinition with separate names and types lists to reference existing beans or instantiate new classes.
  • The AbstractRestClientFactoryBean automatically wires interceptors into the client builder at lines 37-41 of its source file.
  • Interceptors execute sequentially in declaration order: first names, then types.
  • All configuration maps to YAML under coapi.clients.{name}.sync.interceptor, parsed by CoApiProperties.

Frequently Asked Questions

What is the execution order of CoApi interceptors?

Interceptors execute strictly in the order they appear in your configuration. The framework processes all entries in the names list first, followed by all entries in the types list. This sequence determines the chain of responsibility for modifying requests and responses.

Can I modify the response body using CoApi interceptors?

Yes. Inside your intercept method, call execution.execute(request, body) to obtain the ClientHttpResponse, then wrap or transform the response body before returning it. The returned ClientHttpResponse propagates back through the interceptor chain to your service interface.

How do I disable an interceptor for a specific CoApi client?

Remove the interceptor's bean name from the names list or its class from the types list in your YAML configuration for that specific client. Because configuration is per-client under coapi.clients.{clientName}, other clients remain unaffected.

Where is the interceptor configuration validated in the source code?

The YAML configuration binding to ClientProperties objects occurs in CoApiProperties.kt at lines 48-64. Unit tests verifying this mapping, including interceptor configuration, are located in CoApiPropertiesTest.kt at lines 112-114 according to the ahoo-wang/coapi source.

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 →