# How to Add Custom Headers to a Specific CoApi Client Using WebClientBuilderCustomizer

> Learn how to add custom headers to a specific CoApi client in Spring using WebClientBuilderCustomizer. Customize CoApi requests by overriding the customize method with defaultHeader or defaultHeaders.

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

---

**Implement a Spring bean that implements `WebClientBuilderCustomizer`, override the `customize` method to call `defaultHeader()` or `defaultHeaders()` on the `WebClient.Builder`, and filter by `CoApiDefinition` to target a specific client.**

CoApi generates HTTP clients based on Spring WebClient, and you can intercept the client creation process to inject custom headers that apply to every request. By providing a custom `WebClientBuilderCustomizer` implementation, you modify the underlying `WebClient.Builder` before the final client bean is instantiated, ensuring your headers are present on all outbound calls from that specific CoApi client.

## Understanding the WebClientBuilderCustomizer Extension Point

CoApi delegates WebClient construction to `WebClientFactoryBean`, which accepts one or more `WebClientBuilderCustomizer` implementations to configure the builder. According to the [ahoo-wang/coapi](https://github.com/ahoo-wang/coapi) source code, this interface extends the base `HttpClientBuilderCustomizer` and provides access to both the `CoApiDefinition` (metadata about the client being built) and the `WebClient.Builder` instance.

In [`spring/src/main/kotlin/me/ahoo/coapi/spring/client/reactive/WebClientBuilderCustomizer.kt`](https://github.com/ahoo-wang/coapi/blob/main/spring/src/main/kotlin/me/ahoo/coapi/spring/client/reactive/WebClientBuilderCustomizer.kt), the interface defines a single method:

```kotlin
fun customize(coApiDefinition: CoApiDefinition, builder: WebClient.Builder)

```

The `CoApiDefinition` parameter contains identifying information such as the client name, allowing you to conditionally apply headers only to specific clients while leaving others unaffected.

## Creating a Targeted Customizer for a Single Client

To add custom headers to only one specific CoApi client, implement the customizer interface and filter by the definition name.

### 1. Implement the Interface as a Spring Bean

Create a `@Service` or `@Component` class that implements `WebClientBuilderCustomizer`. Spring automatically detects and registers this bean during component scanning.

### 2. Filter by Client Name

Inside the `customize` method, check `coApiDefinition.name` to determine which client is being constructed. Return early if the definition does not match your target client.

### 3. Apply Default Headers

Use `builder.defaultHeader()` for single headers or `builder.defaultHeaders()` for bulk operations. These methods attach headers to every request made by the resulting WebClient.

```kotlin
package me.ahoo.coapi.example.consumer

import me.ahoo.coapi.spring.CoApiDefinition
import me.ahoo.coapi.spring.client.reactive.WebClientBuilderCustomizer
import org.springframework.http.HttpHeaders
import org.springframework.stereotype.Service
import org.springframework.web.reactive.function.client.WebClient

@Service
class GithubClientHeaderCustomizer : WebClientBuilderCustomizer {

    override fun customize(coApiDefinition: CoApiDefinition, builder: WebClient.Builder) {
        // Target only the "github" client
        if (coApiDefinition.name != "github") {
            return
        }

        // Add authentication header
        builder.defaultHeader(HttpHeaders.AUTHORIZATION, "Bearer my-static-token")
        
        // Add custom metadata headers
        builder.defaultHeader("X-Client-Id", "example-consumer")

        // Or configure multiple headers at once
        builder.defaultHeaders { httpHeaders ->
            httpHeaders.addAll(
                mapOf(
                    "X-Request-Source" to "coapi-consumer",
                    "X-Trace-Id" to "generated-trace-id"
                )
            )
        }
    }
}

```

## Applying Headers Globally to All CoApi Clients

If you want the same headers added to every CoApi client in your application, omit the name check. This approach is useful for correlation IDs, global API keys, or user-agent strings that should be universal across all HTTP clients.

```kotlin
@Service
class GlobalHeaderCustomizer : WebClientBuilderCustomizer {
    
    override fun customize(coApiDefinition: CoApiDefinition, builder: WebClient.Builder) {
        // Applies to every CoApiDefinition
        builder.defaultHeader("X-Global-Header", "globalValue")
        builder.defaultHeader("X-Application-Name", "my-service")
    }
}

```

## How the Initialization Flow Works

Understanding the bean creation sequence helps debug why headers might not appear as expected. The process involves four key components from the `ahoo-wang/coapi` repository:

1. **`CoApiRegistrar`** ([`spring/src/main/kotlin/me/ahoo/coapi/spring/CoApiRegistrar.kt`](https://github.com/ahoo-wang/coapi/blob/main/spring/src/main/kotlin/me/ahoo/coapi/spring/CoApiRegistrar.kt)): Scans the application context for all `CoApiDefinition` beans and triggers the creation of corresponding client beans.

2. **`WebClientFactoryBean`** ([`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)): For each definition, this factory bean creates the `WebClient` instance. It retrieves all `WebClientBuilderCustomizer` beans from the Spring context.

3. **`AbstractWebClientFactoryBean`** ([`spring/src/main/kotlin/me/ahoo/coapi/spring/client/reactive/AbstractWebClientFactoryBean.kt`](https://github.com/ahoo-wang/coapi/blob/main/spring/src/main/kotlin/me/ahoo/coapi/spring/client/reactive/AbstractWebClientFactoryBean.kt)): The base implementation that orchestrates the builder configuration. It iterates through the collected customizers and invokes `customize(coApiDefinition, builder)` for each one.

4. **`WebClientBuilderCustomizer`**: Your implementation modifies the builder before `AbstractWebClientFactoryBean` calls `builder.build()` to create the final `WebClient` bean.

This architecture ensures that headers are injected at the lowest level of the HTTP stack, guaranteeing they appear on every request regardless of which CoApi client method is invoked.

## Summary

- **Implement `WebClientBuilderCustomizer`** as a Spring-managed bean to intercept WebClient construction.
- **Use `defaultHeader()` or `defaultHeaders()`** on the `WebClient.Builder` to attach static headers to all requests.
- **Filter by `CoApiDefinition.name`** to restrict header injection to a specific CoApi client while leaving others unchanged.
- **Rely on `CoApiRegistrar` and `WebClientFactoryBean`** to automatically detect and apply your customizer during the client initialization phase.

## Frequently Asked Questions

### Can I add dynamic headers that change per request?

The `defaultHeader` and `defaultHeaders` methods apply static values to every request. For dynamic headers that vary per request (such as timestamps or request-scoped tokens), use WebClient's `ExchangeFilterFunction` instead, which can modify the `ClientRequest` on each execution. You can add filters via the same `WebClient.Builder` inside your customizer using `builder.filter()`.

### How do I target multiple specific clients with one customizer?

Inside the `customize` method, maintain a set of target client names and check if `coApiDefinition.name` is contained within that set. Alternatively, use pattern matching or check other properties of the `CoApiDefinition` object to determine if the customizer should apply headers to the current client being built.

### What is the difference between `defaultHeader` and `defaultHeaders`?

`defaultHeader(key, value)` adds a single header key-value pair to every request. `defaultHeaders(Consumer<HttpHeaders>)` accepts a lambda that receives the `HttpHeaders` object, allowing you to add multiple headers, manipulate existing ones, or conditionally set headers based on logic within the consumer block. Both methods store headers in the underlying `WebClient` builder.

### Where does the customizer fit in the Spring bean lifecycle?

The customizer is invoked during the instantiation phase of the CoApi client bean, specifically when `WebClientFactoryBean` creates the `WebClient` instance. This occurs after Spring's dependency injection container has initialized the customizer bean itself but before the CoApi client proxy is fully constructed and injected into your service classes. This timing ensures the headers are baked into the WebClient before any HTTP calls are made.