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

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:

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

For Maven:

<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/api/src/main/kotlin/me/ahoo/coapi/api/CoApi.kt).

Before (Feign):

@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):

@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/spring/src/main/kotlin/me/ahoo/coapi/spring/EnableCoApiRegistrar.kt).

@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/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.

@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/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 or application.properties. Replace them with standard Spring WebClient properties, or keep the same property names for base URLs.

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/spring/src/test/kotlin/me/ahoo/coapi/spring/CoApiContextTest.kt) that validate both reactive and synchronous bean creation.

@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 class holds resolved client metadata including base URL, load-balancing flags, and bean names. The EnableCoApiRegistrar 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/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.

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.

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 →