Home›Java›Spring Cloud Feign Client: Declarative HTTP in Microservices
Intermediate
7 min · May 23, 2026
Spring Cloud Feign Client: Declarative HTTP in Microservices
Master Spring Cloud OpenFeign: @FeignClient configuration, ErrorDecoder, RequestInterceptor for auth, Resilience4j fallbacks, and Retryer for production-grade HTTP clients..
N
NarenFounder & Principal Engineer
20+ years shipping production Java in banking & fintech. Drawn from code that ran under real load.
Declare HTTP clients with @FeignClient(name, url, fallback/fallbackFactory) and interface methods mirroring Spring MVC annotations
Configure timeouts via FeignConfig with Request.Options(connectTimeout, readTimeout)
Map HTTP error responses to exceptions using ErrorDecoder interface implementations
Add auth headers to every request with @RequestInterceptor beans
Use FallbackFactory (not fallback) so your fallback receives the exception that triggered it
✦ Definition~90s read
What is Feign Client in Spring Boot Microservices?
Spring Cloud OpenFeign is a declarative REST client framework that generates HTTP client implementations from annotated Java interfaces at application startup. It integrates with Spring Cloud LoadBalancer for service-discovery-based load balancing, Micrometer for metrics, Resilience4j for circuit breaking and fallbacks, and Spring Security for propagating security contexts.
★
Feign is a translator that converts your Java method calls into HTTP requests.
Under the hood, Feign uses a JDK dynamic proxy to implement the annotated interface. When a method is called, the proxy inspects the annotations to build an HTTP request (method, URL, headers, body), executes it via a Feign Client (backed by HttpClient, OkHttp, or Java 11 HttpClient), and deserializes the response using Jackson (or the configured decoder).
The entire process is synchronous by default, though Spring Cloud also supports reactive Feign with WebClient.
Feign clients are identified by their name (or serviceId), which is the Spring application name of the target service in Eureka/Consul. When a name is used without a url, Feign routes requests through Spring Cloud LoadBalancer for automatic instance selection. When a url is specified, Feign skips service discovery and calls the URL directly — useful for external APIs or local development.
Plain-English First
Feign is a translator that converts your Java method calls into HTTP requests. Instead of writing boilerplate code to build URLs, set headers, serialize request bodies, and parse responses, you define an interface that looks exactly like a Spring MVC controller — and Feign does all the HTTP work behind the scenes.
Every microservice team eventually writes the same boilerplate HTTP client code: URL concatenation, header setting, request body serialization, response deserialization, error status code checking, and retry logic. With RestTemplate this means hundreds of lines of infrastructure code per service integration. With WebClient it's better but still requires significant ceremony for each new endpoint.
Spring Cloud OpenFeign eliminates this ceremony entirely. You declare an interface annotated with @FeignClient, define methods that mirror the downstream service's REST API using the same Spring MVC annotations you use in controllers (@GetMapping, @PostMapping, @PathVariable, @RequestBody), and Spring Cloud generates a working HTTP client implementation at startup. The entire integration layer for a CRUD service is typically under 30 lines.
The production pain points begin when things go wrong. How does Feign handle a 404? By default it throws FeignException — which loses the HTTP status code in the stack trace and makes it impossible to distinguish 'not found' from 'server error' without parsing exception messages. ErrorDecoder solves this by mapping HTTP status codes to typed business exceptions.
Authentication is another friction point. If your downstream services require JWT Bearer tokens, API keys, or HMAC signatures, you need to add headers to every request. @RequestInterceptor lets you register beans that run before every Feign request, adding headers from the current security context, environment, or computed signatures — without touching each individual Feign method.
Resilience is the hardest part. When a downstream service is slow or unavailable, you need fallbacks. Feign integrates with Resilience4j via Spring Cloud CircuitBreaker, and the FallbackFactory pattern gives fallbacks access to the exception that triggered them — essential for distinguishing circuit-open (return cached data) from authentication failure (propagate the 401).
This guide covers all of these patterns with production code, the exact configuration that works in Spring Boot 3.x, and the debug commands you need when Feign misbehaves in production.
@FeignClient Annotation and Basic Configuration
The @FeignClient annotation marks an interface as a Feign client and provides the metadata needed to generate the HTTP client implementation. The name attribute is the logical service name used for load balancing. The url attribute bypasses service discovery and calls the specified URL directly. The configuration attribute points to a configuration class that customizes the Feign client (encoder, decoder, logger, interceptors, error decoder, timeouts). The fallback or fallbackFactory attributes specify the fallback implementation.
The interface methods must mirror the downstream service's REST API. Use @GetMapping, @PostMapping, @PutMapping, @DeleteMapping from Spring MVC. Path variables use @PathVariable, query parameters use @RequestParam, request bodies use @RequestBody, and headers use @RequestHeader. For multipart file uploads, use @RequestPart.
Name collisions between multiple Feign clients that call the same service name can cause issues. If two @FeignClient interfaces have the same name attribute but different configurations, Spring will throw a bean definition conflict. Use contextId to give each client a unique bean name while keeping the same service name for routing.
Feign is not reactive by default. For reactive applications using WebFlux, use the spring-cloud-starter-openfeign with the feign-reactor library, or prefer Spring WebClient with @LoadBalanced for fully reactive inter-service communication. Mixing synchronous Feign in a reactive stack causes thread pool pressure.
Multiple FeignClients With the Same name Need contextId
If two @FeignClient interfaces share the same name attribute, Spring throws BeanDefinitionStoreException at startup. Use the contextId attribute to give each a unique Spring bean name while keeping the same name for service discovery routing.
Production Insight
Always configure per-client timeouts in spring.cloud.openfeign.client.config; never rely on the defaults which can be effectively infinite depending on the underlying HTTP client.
Key Takeaway
@FeignClient generates HTTP client implementations at startup; use contextId for multiple clients to the same service, configure timeouts explicitly, and enable circuitbreaker.enabled=true for fallback support.
thecodeforge.io
Spring Cloud Feign Client
FeignConfig: Timeouts, Logging, and Custom Components
Feign configuration classes are regular Spring @Configuration classes that define beans consumed by the Feign infrastructure. Unlike most Spring beans, Feign configuration beans are scoped — if you specify a class in @FeignClient(configuration=MyConfig.class), those beans are only active for that specific client. Global defaults go in the default section of spring.cloud.openfeign.client.config YAML or in a configuration class annotated with @Configuration (without the @FeignClient reference) and placed on the component scan path.
Timeout configuration via Request.Options is the most important config. connectTimeout is the time to establish a TCP connection; readTimeout is the time to wait for a response after the connection is established. These should be sized to your downstream service's P99 response time plus a safety margin, and should be lower than any circuit breaker slow-call threshold.
Feign Logger Level controls what HTTP traffic is logged: NONE (nothing), BASIC (request method/URL and response status/time), HEADERS (plus request/response headers), FULL (plus request/response body). Use BASIC in production for audit trails and FULL only in development or when debugging a specific issue (it logs full bodies which can be large and contain sensitive data).
Custom encoder and decoder beans let you handle non-standard content types or response formats. For example, if a downstream service returns XML, add a JAXB decoder. For form-encoded requests, add a FormEncoder. The default encoder/decoder pair handles JSON via Jackson.
Feign Config Classes Are NOT @Component Scanned by Default
When a @Configuration class is referenced in @FeignClient(configuration=...), it should NOT be in the main component scan path (@ComponentScan) — if it is, the beans become global instead of client-scoped. Place Feign config classes outside your main package or use the spring.cloud.openfeign.client.config YAML approach to avoid accidental global scope.
Production Insight
Enable OkHttp (spring.cloud.openfeign.okhttp.enabled=true) instead of the default Apache HttpClient; OkHttp has better connection pooling and HTTP/2 support.
Key Takeaway
Feign configuration classes are client-scoped when referenced in @FeignClient; do not place them in the main component scan to avoid accidental global application of client-specific settings.
ErrorDecoder: Mapping HTTP Errors to Business Exceptions
Feign's default behavior for non-2xx responses is to throw a FeignException with the status code embedded in the message string. This is problematic for three reasons: (1) your business logic must parse string messages to determine the error type, (2) the exception hierarchy doesn't convey intent (a 404 and a 500 both throw FeignException), and (3) the response body (often containing error details) may not be easily accessible.
ErrorDecoder provides a hook that runs after receiving a non-2xx response. It receives a Request and Response object and returns an Exception. The returned exception is what gets thrown to your calling code. By implementing ErrorDecoder, you can map status codes to your domain exceptions, extract error details from the response body, and handle retryable errors by returning a RetryableException (which triggers Feign's Retryer).
The most important error categories to handle are: 400 (Bad Request — deserialize the error body into your error response DTO and throw a ValidationException), 401 (Unauthorized — throw an AuthenticationException so the caller can refresh tokens), 403 (Forbidden — throw an AccessDeniedException), 404 (Not Found — throw a ResourceNotFoundException with the resource type and ID), 429 (Too Many Requests — return a RetryableException with the Retry-After header value), and 5xx (Service errors — throw a ServiceException and optionally return a RetryableException for idempotent operations).
RetryableException is special — when ErrorDecoder returns a RetryableException, Feign's configured Retryer will retry the request. Use this for transient errors (502, 503, 504, connection resets) but NEVER for POST/PUT/DELETE unless you have idempotency guarantees.
RetryableException on POST Causes Duplicate Requests
Feign's Retryer retries the entire request, including POST and PUT. Returning a RetryableException from ErrorDecoder for 5xx errors on non-idempotent methods can cause duplicate operations. Always check the HTTP method before making an exception retryable, or implement a custom Retryer that skips retries for non-GET methods.
Production Insight
Always consume and close the response body in ErrorDecoder; leaving it open leaks connections in the underlying HTTP client's connection pool.
Key Takeaway
ErrorDecoder converts HTTP status codes to typed business exceptions; use RetryableException only for idempotent methods and transient errors like 429 and 503.
thecodeforge.io
Spring Cloud Feign Client
@RequestInterceptor for Authentication Headers
RequestInterceptor provides a pre-request hook that runs before every Feign request made by clients that include it in their configuration. It receives a RequestTemplate that you can modify to add headers, change the URL, add query parameters, or modify the request body. This is the canonical way to add authentication tokens to all Feign requests without cluttering individual method signatures with @RequestHeader parameters.
For JWT propagation, the interceptor reads the current user's JWT from the SecurityContext and adds it as a Bearer token. This works for synchronous request flows where the SecurityContext is populated. The critical caveat is async execution — any @Async or CompletableFuture boundary clears the SecurityContext in a new thread. You must explicitly propagate the context across async boundaries.
For service-to-service authentication with a dedicated service account (not user JWT propagation), use a separate bean that maintains a cached service token with automatic refresh. The interceptor checks the token's expiry, refreshes it if needed, and adds the valid token. Use a ReentrantLock or AtomicReference for thread-safe token refresh.
For HMAC-signed requests (common for payment APIs like Stripe), the interceptor computes the signature from the request body and timestamp and adds it as a header. The body must be read from the RequestTemplate, the signature computed using HMAC-SHA256, and the result added as a header — all before the request is sent.
SecurityContext Is Not Propagated Across @Async Boundaries
If you call a Feign client from an @Async method or a CompletableFuture callback, SecurityContextHolder.getContext() returns an empty context. Capture the Authentication before the async boundary and set it in a DelegatingSecurityContextRunnable wrapper, or use SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL) to propagate via InheritableThreadLocal.
Production Insight
Always add a correlation ID header (X-Request-ID from MDC) in your RequestInterceptor so distributed traces can be correlated across service boundaries without Zipkin.
Key Takeaway
RequestInterceptor is the canonical way to add auth headers; implement token caching with a 30-second refresh buffer and double-checked locking for service-to-service auth scenarios.
Resilience4j Fallback Factory and Circuit Breaker Integration
Spring Cloud OpenFeign integrates with Resilience4j via Spring Cloud CircuitBreaker when spring.cloud.openfeign.circuitbreaker.enabled=true. This enables the fallback and fallbackFactory attributes on @FeignClient. The critical difference between fallback and fallbackFactory is that fallbackFactory receives the exception that triggered the fallback, while fallback gets none — this makes fallbackFactory vastly more useful in production.
With FallbackFactory, your fallback implementation can make intelligent decisions: if the exception is a ResourceNotFoundException (404), re-throw it (no point in returning a fallback for 'order not found'). If it's a ServiceUnavailableException or ConnectTimeoutException, return cached data or a degraded response. If it's an AccessDeniedException, propagate it. This logic is impossible with a simple fallback class.
The circuit breaker name used by Feign is derived from the FeignClient interface name by default in Spring Cloud 2022.x. You can override it per-method using @CircuitBreaker annotations. Each circuit breaker has its own state machine and metrics, so a timeout on the payment service doesn't affect the circuit breaker for the inventory service.
Fallback implementations must be Spring beans — annotate them with @Component. The fallback for a FeignClient must implement the same interface. Each method returns the fallback value for that operation. Methods that cannot return a meaningful fallback (write operations that must succeed) should throw an exception from the fallback to propagate the failure to the caller.
FallbackFactory Is Almost Always Better Than fallback
The fallback attribute gives your fallback zero information about why it was triggered. FallbackFactory gives you the exception, allowing conditional logic: re-throw business exceptions (404, 403), return cached data for infrastructure failures (timeout, connection refused), and propagate auth errors. Use FallbackFactory in all production code.
Production Insight
Cache read responses (inventory, catalog, user profiles) in Redis from successful Feign calls; use those cached values in fallback methods to provide a degraded-but-functional experience during outages.
Key Takeaway
Use FallbackFactory over fallback always; make fallback decisions based on exception type to distinguish business errors (re-throw) from infrastructure failures (return degraded response).
Micrometer Metrics and Distributed Tracing with Feign
Spring Cloud OpenFeign automatically integrates with Micrometer to produce feign.client.requests metrics tagged with client name, method, host, and outcome (SUCCESS, CLIENT_ERROR, SERVER_ERROR). These metrics are invaluable for identifying slow or failing service integrations without looking at individual logs.
Key metrics to dashboard: feign.client.requests by outcome (track error rates per client), feign.client.requests percentile latency (P95, P99 per client), and feign.client.requests count (request rates). Alert on feign.client.requests{outcome='SERVER_ERROR'} exceeding a threshold for sustained periods.
Distributed tracing propagates automatically when Micrometer Tracing is on the classpath. The trace ID and span ID from the current request context are injected into outgoing Feign request headers (B3 headers: X-B3-TraceId, X-B3-SpanId, X-B3-Sampled, or W3C Trace-Context headers depending on configuration). The downstream service's Micrometer Tracing picks up these headers and continues the trace, linking the spans in Zipkin or Jaeger.
Custom span tags add business context to traces: add the order ID, user ID, or product ID to the span so traces are searchable by business identifiers in the trace UI. Use the Tracer bean to create custom spans within complex operations that span multiple Feign calls.
Trace IDs in MDC for Log Correlation
With Micrometer Tracing, the current traceId and spanId are automatically populated in MDC (Mapped Diagnostic Context). Include %X{traceId} and %X{spanId} in your log pattern to correlate logs with traces in Zipkin. This allows you to find all logs for a specific distributed request by searching for the trace ID in your log aggregation system.
Production Insight
Set feign.client.config.default.logger-level=BASIC and logging.level.{your.feign.package}=DEBUG to log method+URL+status+time for every Feign call without logging sensitive request/response bodies.
Key Takeaway
Feign integrates automatically with Micrometer for metrics and distributed tracing; add custom span tags with business identifiers so traces are searchable by order ID or user ID in Zipkin.
Don't Fly Blind: Why You Need Request Interceptors Before They Burn You
You just shipped a Feign client that works perfectly on your local machine. Then it hits staging, and every call returns 401. Classic: you forgot to propagate the auth token. Request interceptors are the hook that runs before every outbound call. They are not optional for any real service. The RequestInterceptor interface gives you a RequestTemplate — that's your chance to inject headers, query parameters, or even mutate the URI. The HOW is trivial: implement apply and stuff in the header. The WHY is survival: without interceptors, you hardcode tokens in constants, leak secrets, and miss auth changes. Spring Cloud Feign also ships BasicAuthRequestInterceptor for HTTP basic auth. It's a one-liner bean. Use it. But never roll your own auth interceptor without reading the token from a SecurityContext or vault. If you hardcode the secret, I will find you.
TokenPropagationInterceptor.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// io.thecodeforge — spring feign interceptor for jwt propagation
@ComponentpublicclassTokenPropagationInterceptorimplementsRequestInterceptor {
private final TokenProvider tokenProvider; // from your security libpublicTokenPropagationInterceptor(TokenProvider tokenProvider) {
this.tokenProvider = tokenProvider;
}
@Overridepublicvoidapply(RequestTemplate template) {
Optional<String> token = tokenProvider.getCurrentToken();
token.ifPresent(t -> template.header("Authorization", "Bearer " + t));
}
}
Output
Every Feign client call now carries the current user's JWT.
No more 401s in staging. No more 'forgot to set the header.'
Production Trap:
Never store the token as a field in the interceptor. It's per-thread context. Use RequestContextHolder or a reactive context to fetch it per-call. Stale tokens cause intermittent auth failures that are a nightmare to debug.
Key Takeaway
Always inject auth headers via an interceptor. Never hardcode them. Your production pipeline will reject your deploy otherwise.
Your Feign Client Is A Liar: Why Custom Beans Configuration Matters
Default Feign configuration works fine for JSON over HTTP. That's it. The second you need XML, a custom ObjectMapper, or a non-standard Decoder, defaults will silently fail or produce garbage. Spring Cloud lets you override any bean via FeignClientsConfiguration. The WHY is brutal: if your API returns application/xml, Feign's default Jackson decoder throws an unhelpful HttpMessageNotReadableException. You waste hours. Instead, supply a custom Decoder bean. Same for Encoder, Contract, Logger.Level. You configure per-client or globally. The HOW is a @Configuration class referenced in @FeignClient(configuration = MyConfig.class). Put it in a package NOT scanned by @ComponentScan unless you want global leakage. Spring Boot 3.x also supports spring.cloud.openfeign.client.config.feignName.* properties. Use that for environment-specific tuning — timeouts, log levels, retries. Keep code for logic, properties for config.
FeignCustomConfig.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// io.thecodeforge — per-client feign configurationimport feign.Logger;
import feign.codec.Decoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ConfigurationpublicclassPaymentClientConfig {
@BeanpublicLogger.LevelfeignLoggerLevel() {
return Logger.Level.FULL; // log headers & body in debug
}
@BeanpublicDecodercustomDecoder() {
return new PaymentDecoder(); // handles custom XML envelope
}
}
Output
PaymentClient now logs entire requests and decodes XML payloads.
Default decoder never tried. Parser exceptions gone.
Pro Move:
Use @FeignClient(configuration = PaymentClientConfig.class) on the interface. Keep the config class outside the main scan package to avoid it becoming the global default.
Key Takeaway
Override Feign beans for any non-JSON API or custom serialization. Defaults are for demos, not production.
● Production incidentPOST-MORTEMseverity: high
Feign Default Timeout Caused Cascading Thread Pool Exhaustion
Symptom
Order processing latency jumped from 150ms P99 to 45 seconds. Thread pool utilization hit 100%. New order requests started queueing and then timing out. Inventory service pods showed no CPU or memory pressure.
Assumption
The team assumed Feign clients had a default timeout and the issue was with the downstream supplier API, not the Feign configuration.
Root cause
Feign's default timeout is 10 seconds for connect and 60 seconds for read — but Spring Boot auto-configuration with the feign-okhttp dependency had inadvertently disabled these defaults, resulting in an effectively infinite read timeout. The supplier API's response time degraded to 90 seconds during a batch job on their end. Every Feign thread in the inventory service's thread pool was blocked waiting for responses, and with the default Tomcat thread pool of 200 threads, all were exhausted within 2 minutes.
Fix
Added explicit Request.Options with connectTimeout=2s and readTimeout=5s to the Feign client configuration. Added Resilience4j circuit breaker with a 3-second slow-call threshold. Added a FallbackFactory that returned cached inventory data for up to 5 minutes during supplier outages.
Key lesson
Never rely on Feign's default timeouts.
Always configure explicit connect and read timeouts for every Feign client, sized to your SLA requirements.
Add circuit breakers with slow-call detection to prevent slow dependencies from blocking your thread pool.
Production debug guideSymptom → root cause → fix5 entries
Symptom · 01
Feign client throws FeignException instead of a typed business exception
→
Fix
By default Feign wraps all non-2xx responses in FeignException. Implement an ErrorDecoder that maps specific HTTP status codes to business exceptions: 404 → ResourceNotFoundException, 409 → ConflictException, 503 → ServiceUnavailableException. Register the ErrorDecoder as a @Bean in a @Configuration class or as a component in the FeignClient's configuration attribute. After implementing, verify the exception type in your catch blocks — FeignException should no longer appear.
Symptom · 02
Authentication headers missing from Feign requests despite RequestInterceptor configuration
→
Fix
Verify the RequestInterceptor is a Spring bean — either annotated with @Component or defined as a @Bean in a @Configuration class. Check that the FeignClient's configuration attribute doesn't specify a configuration class that overrides or excludes the interceptor. Also verify that the SecurityContext is populated when the Feign call is made — in async contexts (CompletableFuture, @Async) the security context is not propagated by default; you need to capture it before the async boundary and restore it inside.
Symptom · 03
Feign client fallback is never triggered even when the downstream service is down
→
Fix
Check that spring.cloud.openfeign.circuitbreaker.enabled=true is set — fallbacks require circuit breaker integration and are not triggered on simple connection failures without it. Also verify the FeignClient's fallback or fallbackFactory class is a Spring bean. Check Resilience4j configuration for the circuit breaker named after your FeignClient — the circuit must actually open (failure threshold met) before fallbacks trigger. Use GET /actuator/circuitbreakers to see circuit breaker states.
Symptom · 04
Feign requests are very slow; each request takes noticeably longer than the backend responds
→
Fix
Feign uses Apache HttpClient by default, which creates a new connection for each request unless connection pooling is configured. Add the feign-httpclient or feign-okhttp dependency and configure connection pooling. Check if keepAlive is enabled and that the connection pool size is appropriate for your request rate. Also verify that DNS resolution is not being done per-request — use a caching DNS resolver or prefer IP-based service discovery.
Symptom · 05
Feign client in @Async method throws LazyInitializationException or context not found
→
Fix
The @Async method runs in a separate thread pool that does not have the SecurityContext, RequestContext, or other thread-local data. If your RequestInterceptor reads from SecurityContextHolder, it will fail in async contexts. Capture the security context before the async boundary: Authentication auth = SecurityContextHolder.getContext().getAuthentication(); then in the async method, restore it with SecurityContextHolder.getContext().setAuthentication(auth).
★ Debug Cheat SheetFast diagnosis for Feign client issues in production
Feign calls failing with connection refused / no instances available−
Set logging.level.feign=DEBUG and feign.client.config.default.loggerLevel=FULL in application.yml to see full request/response bodies
Feign vs WebClient vs RestTemplate for Inter-Service Communication
Feature
Feign Client
WebClient
RestTemplate
Programming model
Declarative (interface)
Reactive (Mono/Flux)
Imperative (synchronous)
Boilerplate
Minimal
Medium
High
Reactive support
No (needs feign-reactor)
Native
No
Load balancing
lb:// via LoadBalancer
@LoadBalanced annotation
@LoadBalanced annotation
Fallback/CB
FallbackFactory + Resilience4j
Manual Resilience4j
Manual Resilience4j
Test support
MockitoAnnotations / WireMock
MockWebServer / WireMock
MockRestServiceServer
Spring Boot 3.x status
Supported
Preferred for reactive
Deprecated (use WebClient)
Best for
Synchronous service-to-service
Reactive / streaming APIs
Legacy apps only
⚙ Quick Reference
2 commands from this guide
File
Command / Code
Purpose
TokenPropagationInterceptor.java
@Component
Don't Fly Blind
FeignCustomConfig.java
@Configuration
Your Feign Client Is A Liar
Key takeaways
1
Use FallbackFactory over fallback in all production code so fallback methods can make decisions based on the causing exception
2
Always configure explicit connect and read timeouts per Feign client; never rely on defaults which can be effectively infinite with some HTTP client configurations
3
ErrorDecoder is the correct pattern for mapping HTTP status codes to typed business exceptions; return RetryableException only for idempotent GET requests
4
RequestInterceptor is the canonical pattern for cross-cutting headers (auth tokens, correlation IDs) across all Feign calls from a client
5
Multiple @FeignClient interfaces targeting the same service must use contextId to avoid Spring bean definition conflicts
Common mistakes to avoid
6 patterns
×
Using fallback instead of fallbackFactory
Symptom
Fallback always returns the same response regardless of whether the cause was a 404, timeout, or circuit open state
Fix
Use fallbackFactory = MyFallbackFactory.class so your fallback receives the cause exception and can make conditional decisions
×
Not configuring explicit timeouts on Feign clients
Symptom
Slow downstream services exhaust thread pools, causing cascading failures across the calling service
Fix
Always set connect-timeout and read-timeout in spring.cloud.openfeign.client.config per client; never rely on defaults
×
Placing FeignConfig in the main component scan package
Symptom
FeignConfig beans (ErrorDecoder, Request.Options) apply globally to all Feign clients instead of the intended one
Fix
Keep FeignConfig classes outside the main package or explicitly exclude them from @ComponentScan; reference them only via @FeignClient(configuration=...)
×
Calling Feign client from @Async method without propagating SecurityContext
Symptom
RequestInterceptor gets empty SecurityContextHolder and JWT is not added to outgoing requests; downstream returns 401
Fix
Capture Authentication before @Async boundary and restore inside, or use DelegatingSecurityContextExecutorService for the async executor
×
Returning RetryableException from ErrorDecoder for POST/PUT requests
Symptom
Duplicate records created, double charges processed, or idempotency violations during retry storms
Fix
Check the HTTP method in ErrorDecoder before returning RetryableException; only return it for GET/HEAD requests
×
Not consuming the response body in ErrorDecoder
Symptom
Connection pool leaks over time; 'Connection pool shutdown' errors under sustained load
Fix
Always read and close the response body in ErrorDecoder using Util.toString(response.body().asReader(...)); the body must be consumed to release the HTTP connection
INTERVIEW PREP · PRACTICE MODE
Interview Questions on This Topic
Q01SENIOR
What is the difference between @FeignClient fallback and fallbackFactory...
Q02SENIOR
How do you prevent a Feign client from being affected by SecurityContext...
Q03SENIOR
When should you use a Feign Retryer versus a Resilience4j retry policy?
Q04SENIOR
How does Feign integrate with Spring Cloud LoadBalancer?
Q05SENIOR
How do you test a Feign client in isolation?
Q06SENIOR
What happens to Feign's circuit breaker when a service returns 404?
Q07JUNIOR
How do you configure different timeouts for different Feign clients?
Q08SENIOR
What is the purpose of the contextId attribute in @FeignClient?
Q01 of 08SENIOR
What is the difference between @FeignClient fallback and fallbackFactory?
ANSWER
The fallback attribute specifies a class that implements the Feign interface and provides default responses. It has no information about why the fallback was triggered. The fallbackFactory attribute specifies a FallbackFactory that creates the fallback implementation dynamically and passes the causing exception. This allows conditional logic: re-throw 404/403 exceptions (they're business errors, not service failures), return cached data for connection timeouts, and propagate auth errors. FallbackFactory should be used in all production code.
Q02 of 08SENIOR
How do you prevent a Feign client from being affected by SecurityContext propagation issues in async code?
ANSWER
SecurityContextHolder uses ThreadLocal by default, which is not propagated across thread boundaries in @Async methods or CompletableFuture callbacks. Solutions: (1) Change the SecurityContext storage strategy to MODE_INHERITABLETHREADLOCAL, which propagates to child threads. (2) Capture the Authentication before the async boundary and manually set it inside the async method wrapped in a DelegatingSecurityContextRunnable. (3) Use Spring Security's DelegatingSecurityContextExecutorService as the async executor bean. Option 2 is the most explicit and controllable.
Q03 of 08SENIOR
When should you use a Feign Retryer versus a Resilience4j retry policy?
ANSWER
Feign's Retryer triggers on RetryableException thrown by ErrorDecoder — it retries at the HTTP client level, meaning the same Feign client makes the retry. Resilience4j retry policy decorates the method call at the application level and is configured separately. Key differences: Feign Retryer is simpler for retry-on-connection-failure; Resilience4j provides more sophisticated backoff, jitter, and per-exception configuration. In Spring Cloud with Resilience4j on the classpath, prefer Resilience4j retry for consistency with circuit breaker configuration. Never use both simultaneously on the same client — you'll get nested retries and exponential failure amplification.
Q04 of 08SENIOR
How does Feign integrate with Spring Cloud LoadBalancer?
ANSWER
When @FeignClient(name='service-name') is used without a url attribute, Feign builds a URL using lb://service-name. The lb:// scheme is intercepted by Spring Cloud LoadBalancer, which queries the service registry (Eureka/Consul/Kubernetes) for instances with that name, applies the configured load balancing strategy (round-robin by default), and returns a resolved http://ip:port URL. Feign then makes the actual HTTP call to the resolved address. This happens transparently for each request, so new instances become eligible for traffic immediately after registration.
Q05 of 08SENIOR
How do you test a Feign client in isolation?
ANSWER
Use WireMock or MockWebServer to stub the HTTP responses. In Spring Boot tests, use @AutoConfigureWireMock with @SpringBootTest(webEnvironment=RANDOM_PORT). Set the Feign client URL to the WireMock server URL (eureka.client.enabled=false and the client's url pointing to localhost:WIREMOCK_PORT). For unit testing without Spring context, use Feign.builder() to create the client manually with a MockClient. Test ErrorDecoder behavior separately by creating it directly and calling decode() with a mock Response object.
Q06 of 08SENIOR
What happens to Feign's circuit breaker when a service returns 404?
ANSWER
By default, Feign's circuit breaker only records exceptions as failures, not all non-2xx responses. A 404 that Feign wraps in a FeignException will count as a failure. If you use ErrorDecoder and throw a ResourceNotFoundException (which extends RuntimeException, not a recorded exception type), the circuit breaker may not count it as a failure depending on your configuration. Configure resilience4j.circuitbreaker.instances.ClientName.record-exceptions to specify which exception types increment the failure counter. Typically you want to record connection errors and 5xx responses, but NOT 4xx errors which represent client bugs, not service health issues.
Q07 of 08JUNIOR
How do you configure different timeouts for different Feign clients?
ANSWER
Use per-client configuration in YAML: spring.cloud.openfeign.client.config.{clientName}.connect-timeout and .read-timeout, where clientName matches the @FeignClient name or contextId attribute. Alternatively, define a @Configuration class with a Request.Options bean and reference it in @FeignClient(configuration=...). The per-client YAML configuration overrides the global default configuration in spring.cloud.openfeign.client.config.default. Per-client configuration is essential because different services have different SLAs — a payment service needs aggressive timeouts (1-2s) while a report generation service may need 30 seconds.
Q08 of 08SENIOR
What is the purpose of the contextId attribute in @FeignClient?
ANSWER
contextId provides a unique Spring bean name for the Feign client, separate from the name attribute used for service routing. It's required when two @FeignClient interfaces share the same name attribute (because they call the same service) but need different configurations. Without contextId, Spring throws BeanDefinitionStoreException due to duplicate bean definitions. The contextId is also used as the circuit breaker name in Resilience4j integration, so it should be descriptive (e.g., 'productReadClient' vs 'productWriteClient') to make circuit breaker metrics meaningful.
01
What is the difference between @FeignClient fallback and fallbackFactory?
SENIOR
02
How do you prevent a Feign client from being affected by SecurityContext propagation issues in async code?
SENIOR
03
When should you use a Feign Retryer versus a Resilience4j retry policy?
SENIOR
04
How does Feign integrate with Spring Cloud LoadBalancer?
SENIOR
05
How do you test a Feign client in isolation?
SENIOR
06
What happens to Feign's circuit breaker when a service returns 404?
SENIOR
07
How do you configure different timeouts for different Feign clients?
JUNIOR
08
What is the purpose of the contextId attribute in @FeignClient?
SENIOR
FAQ · 6 QUESTIONS
Frequently Asked Questions
01
Does Spring Cloud OpenFeign support Spring Boot 3.x and Java 17?
Yes. Spring Cloud OpenFeign 4.x (Spring Cloud 2022.0+ / Kilburn) supports Spring Boot 3.x with Java 17+. It includes GraalVM native image support. Use the spring-cloud-starter-openfeign dependency with the Spring Cloud BOM for your Spring Boot version.
Was this helpful?
02
Can Feign clients be used in reactive Spring WebFlux applications?
Standard Feign is synchronous and blocking — using it in a WebFlux application ties up non-blocking event loop threads and defeats the purpose of reactive programming. Use Spring's reactive WebClient with @LoadBalanced for reactive inter-service communication. If you must use Feign in a reactive context, use feign-reactor (a third-party extension) or wrap Feign calls in Mono.fromCallable(...).subscribeOn(Schedulers.boundedElastic()).
Was this helpful?
03
How do I log Feign request and response bodies for debugging?
Set feign.client.config.{clientName}.logger-level=FULL in application.yml and add logging.level.{package.of.your.FeignInterface}=DEBUG. FULL level logs complete request headers and body, response headers and body. Warning: this logs sensitive data including auth tokens and request bodies — only enable in development or for short debugging sessions in production, never leave it enabled permanently.
Was this helpful?
04
What is the difference between @FeignClient(url=...) and @FeignClient(name=...)?
url bypasses service discovery entirely and calls the specified URL directly — useful for external APIs (Stripe, Twilio) or fixed URLs in development. name uses the value as a service name in the service registry (Eureka/Consul) and routes through Spring Cloud LoadBalancer for instance selection. You can combine both: name provides the bean name and circuit breaker name while url overrides the routing URL.
Was this helpful?
05
How do I handle multipart file uploads with Feign?
Add the feign-form dependency and configure a FormEncoder bean in your FeignConfig. Use @RequestPart for the file parameter (type MultipartFile or Resource). In the FeignConfig, configure the encoder: new FormEncoder(new SpringFormEncoder()). The @FeignClient method signature should match: @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) ResponseEntity<String> upload(@RequestPart MultipartFile file).
Was this helpful?
06
Can I use Feign clients without Spring Cloud Eureka?
Yes. Set a URL in @FeignClient(url='http://target-service:8080') to bypass service discovery. For Docker Compose environments use service names as hostnames. For Kubernetes without Eureka use Kubernetes DNS names (http://service-name.namespace.svc.cluster.local). You lose automatic load balancing but retain all other Feign features (ErrorDecoder, RequestInterceptor, Resilience4j, Micrometer).