# How to Migrate from Feign or OpenFeign to CoApi: A Step-by-Step Guide

> Migrate from Feign OpenFeign to CoApi effortlessly. Replace annotations, update dependencies, and leverage CoApi for seamless reactive or synchronous HTTP calls with auto-configured WebClient RestClient.

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

---

**To migrate from Feign or OpenFeign to CoApi, replace `@FeignClient` with `@CoApi`, swap `@EnableFeignClients` for `@EnableCoApi`, remove Feign dependencies, and let CoApi auto-configure Spring 6 `WebClient` or `RestClient` beans for reactive and synchronous HTTP calls.**

CoApi is a zero-boilerplate replacement for Spring Cloud OpenFeign built on Spring 6's `@HttpExchange`. Unlike Feign, CoApi supports both reactive and synchronous programming models natively. This guide provides the exact steps to migrate your Spring Boot project using the `ahoo-wang/coapi` source code.

## Step-by-Step Migration Process

### 1. Add the CoApi Starter Dependency

Include the CoApi Spring Boot starter in your build configuration. This brings in the auto-configuration that replaces Feign's `@EnableFeignClients` mechanism.

For Gradle:

```kotlin
implementation("me.ahoo.coapi:coapi-spring-boot-starter")

```

For Maven:

```xml
<dependency>
    <groupId>me.ahoo.coapi</groupId>
    <artifactId>coapi-spring-boot-starter</artifactId>
</dependency>

```

### 2. Remove Feign Dependencies

Delete all OpenFeign-related dependencies from your build file. Remove `spring-cloud-starter-openfeign` and any reactive Feign libraries like `feign-reactive`.

CoApi does not use Feign libraries; it uses Spring 6's `WebClient` under the hood for reactive streams and `RestClient` for blocking calls.

### 3. Replace `@FeignClient` with `@CoApi`

Convert your Feign client interfaces to CoApi by replacing the `@FeignClient` annotation with **`@CoApi`**. The annotation definition resides in [[`CoApi.kt`](https://github.com/ahoo-wang/coapi/blob/main/CoApi.kt)](https://github.com/ahoo-wang/coapi/blob/main/api/src/main/kotlin/me/ahoo/coapi/api/CoApi.kt).

**Before (Feign):**

```kotlin
@FeignClient(name = "github", url = "\${github.url}")
interface GitHubFeignClient {
    @GetMapping("repos/{owner}/{repo}/issues")
    fun getIssues(@PathVariable owner: String, @PathVariable repo: String): List<Issue>
}

```

**After (CoApi):**

```kotlin
@CoApi(baseUrl = "\${github.url}")
interface GitHubApiClient {
    @GetExchange("repos/{owner}/{repo}/issues")
    fun getIssues(@PathVariable owner: String, @PathVariable repo: String): Flux<Issue>
}

```

The **`@CoApi`** annotation supplies the base URL, service ID, or load-balanced name and automatically registers the interface as a Spring bean.

### 4. Enable CoApi Scanning

Add **`@EnableCoApi`** to your `@SpringBootApplication` class or a configuration class, listing the client interfaces to register. The registrar logic is implemented in [[`EnableCoApiRegistrar.kt`](https://github.com/ahoo-wang/coapi/blob/main/EnableCoApiRegistrar.kt)](https://github.com/ahoo-wang/coapi/blob/main/spring/src/main/kotlin/me/ahoo/coapi/spring/EnableCoApiRegistrar.kt).

```kotlin
@EnableCoApi(clients = [GitHubApiClient::class])
@SpringBootApplication
class Application

```

The registrar discovers annotated interfaces and creates the necessary `WebClient` or `RestClient` bean definitions via [[`CoApiRegistrar.kt`](https://github.com/ahoo-wang/coapi/blob/main/CoApiRegistrar.kt)](https://github.com/ahoo-wang/coapi/blob/main/spring/src/main/kotlin/me/ahoo/coapi/spring/CoApiRegistrar.kt).

### 5. Configure Load Balancing (Optional)

If you use service discovery, add `spring-cloud-starter-loadbalancer` and declare the client using `serviceId` or the `lb://` protocol.

```kotlin
@CoApi(serviceId = "order-service")
interface OrderClient {
    @GetExchange("/orders/{id}")
    fun getOrder(@PathVariable id: Long): Mono<Order>
}

```

Alternatively, use `baseUrl = "lb://my-service"`. CoApi detects the `lb://` prefix in [[`CoApiDefinition.kt`](https://github.com/ahoo-wang/coapi/blob/main/CoApiDefinition.kt)](https://github.com/ahoo-wang/coapi/blob/main/spring/src/main/kotlin/me/ahoo/coapi/spring/CoApiDefinition.kt) and creates a load-balanced `WebClient` using Spring Cloud LoadBalancer.

### 6. Update Configuration Properties

Remove Feign-specific properties (e.g., `feign.client.config.*`) from your [`application.yml`](https://github.com/ahoo-wang/coapi/blob/main/application.yml) or `application.properties`. Replace them with standard Spring `WebClient` properties, or keep the same property names for base URLs.

```yaml
github:
  url: https://api.github.com

```

CoApi reads `${...}` placeholders directly from the Spring `Environment`.

### 7. Remove `@EnableFeignClients`

Delete the `@EnableFeignClients` annotation from your main application class. This annotation is no longer needed because `@EnableCoApi` handles client registration.

### 8. Test the Migration

Run your integration tests to verify that CoApi beans are created correctly. The project includes tests such as [[`CoApiContextTest.kt`](https://github.com/ahoo-wang/coapi/blob/main/CoApiContextTest.kt)](https://github.com/ahoo-wang/coapi/blob/main/spring/src/test/kotlin/me/ahoo/coapi/spring/CoApiContextTest.kt) that validate both reactive and synchronous bean creation.

```kotlin
@SpringBootTest
class CoApiContextTest {
    @Autowired
    lateinit var gitHubApiClient: GitHubApiClient

    @Test
    fun `should create Reactive CoApi bean`() {
        assertNotNull(gitHubApiClient)
    }
}

```

### 9. Clean Up

Remove any leftover Feign interfaces, configuration classes, and unused imports. Your project now relies solely on CoApi classes.

## How CoApi Replaces Feign Internally

Understanding the internal mechanics ensures you can debug issues during migration.

**Bean Registration:** The [`CoApiDefinition`](https://github.com/ahoo-wang/coapi/blob/main/spring/src/main/kotlin/me/ahoo/coapi/spring/CoApiDefinition.kt) class holds resolved client metadata including base URL, load-balancing flags, and bean names. The [`EnableCoApiRegistrar`](https://github.com/ahoo-wang/coapi/blob/main/spring/src/main/kotlin/me/ahoo/coapi/spring/EnableCoApiRegistrar.kt) bridges the `@EnableCoApi` annotation with Spring's `ImportBeanDefinitionRegistrar` interface.

**Reactive vs Synchronous:** CoApi inspects return types to determine the client implementation. Interfaces returning `Mono` or `Flux` receive a reactive `WebClient` proxy. Interfaces returning concrete objects receive a synchronous `RestClient` proxy. This happens automatically in [[`CoApiRegistrar.kt`](https://github.com/ahoo-wang/coapi/blob/main/CoApiRegistrar.kt)](https://github.com/ahoo-wang/coapi/blob/main/spring/src/main/kotlin/me/ahoo/coapi/spring/CoApiRegistrar.kt).

**Zero Boilerplate:** Unlike Feign, which requires explicit client builders or configuration classes, CoApi auto-registers clients based solely on the interface and annotation, eliminating manual `@Bean` definitions.

## Summary

- **Remove** `spring-cloud-starter-openfeign` and add `coapi-spring-boot-starter`.
- **Replace** `@FeignClient` with `@CoApi` on your HTTP client interfaces.
- **Swap** `@EnableFeignClients` for `@EnableCoApi(clients = [YourClient::class])`.
- **Support** both reactive (`WebClient`) and synchronous (`RestClient`) patterns without extra configuration.
- **Enable** load balancing using `serviceId` or `lb://` prefixes, resolved by [`CoApiDefinition`](https://github.com/ahoo-wang/coapi/blob/main/spring/src/main/kotlin/me/ahoo/coapi/spring/CoApiDefinition.kt).

## Frequently Asked Questions

### Do I need to change my interface method signatures when migrating to CoApi?

No, you only need to replace the annotation and import statements. Method signatures remain declarative, but you should update Spring MVC annotations (`@GetMapping`) to Spring 6 HTTP exchange annotations (`@GetExchange`). Return types determine whether CoApi uses `WebClient` (for `Mono`/`Flux`) or `RestClient` (for blocking types).

### Can CoApi handle load-balanced clients like OpenFeign with Ribbon?

Yes, CoApi supports load balancing through Spring Cloud LoadBalancer. Specify `serviceId = "my-service"` or `baseUrl = "lb://my-service"` in the `@CoApi` annotation. The framework detects the `lb://` protocol in `CoApiDefinition` and creates a load-balanced `WebClient` automatically.

### Is reactive programming required when using CoApi?

No, CoApi supports both reactive and synchronous programming models. If your interface methods return `Mono` or `Flux`, CoApi configures a reactive `WebClient`. If they return concrete objects, it configures a synchronous `RestClient`. This dual support is a primary advantage over OpenFeign, which lacks native reactive capabilities.

### What happens to my Feign fallback or error decoder implementations?

CoApi does not use Feign's `ErrorDecoder` or fallback mechanisms. You must implement error handling using Spring's `WebClient` filters or `RestClient` interceptors, or use standard Spring retry mechanisms. Remove any Feign-specific fallback classes and replace them with Spring-aware exception handling components.