Unravel โบ Java concepts โบ APIs and web
Declarative HTTP clients (Feign, HTTP interfaces)
You write a Java interface describing a remote REST API with annotations, and the framework generates the HTTP client behind it.
Learn these first
At a glance
A restaurant menu with a phone number.
- 1@FeignClient(name = "inventory", url = "${inventory.url}") on an interface turns it into a beanMethods carry the same mapping annotations as controllers: @GetMapping("/stock/{sku}"), @PathVariable, @RequestBody.
- 2Spring 6 has the same idea built in: an interface with @HttpExchange / @GetExchange methods and an HttpServiceProxyFactory backed by RestClient or WebClient
- 3Timeouts, headers (such as an auth token via RequestInterceptor) and error translation (ErrorDecoder) are configured once per client rather than in every call
- 4Combine with Resilience4j or Spring Cloud CircuitBreaker so a slow remote service does not exhaust your threads
- The default timeouts are often far too generous, or unlimited in older setups
- Because the call looks like a local method call, it is easy to forget it is a network call that can fail, be slow or be retried
- At startup an annotation registrar finds the interfaces and creates a JDK dynamic proxy for each
@FeignClient(name = "inventory", url = "${inventory.url}")public interface InventoryClient { @GetMapping("/stock/{sku}") StockLevel stock(@PathVariable("sku") String sku); @PostMapping("/reservations") Reservation reserve(@RequestBody ReserveRequest request);}- line 1 uses FeignClient, the core of Declarative HTTP clients (Feign, HTTP interfaces)
Read the full explanation (about 2 min)
You write a Java interface describing a remote REST API with annotations, and the framework generates the HTTP client behind it. Calling a method sends the request and parses the response.
A mental model
A restaurant menu with a phone number. You choose a dish by name and the kitchen handles everything; you never describe how to cook it. The interface is the menu, the generated proxy is the waiter on the phone.
How it works
@FeignClient(name = "inventory", url = "${inventory.url}") on an interface turns it into a bean. Methods carry the same mapping annotations as controllers: @GetMapping("/stock/{sku}"), @PathVariable, @RequestBody.
Spring 6 has the same idea built in: an interface with @HttpExchange / @GetExchange methods and an HttpServiceProxyFactory backed by RestClient or WebClient.
Timeouts, headers (such as an auth token via RequestInterceptor) and error translation (ErrorDecoder) are configured once per client rather than in every call.
Combine with Resilience4j or Spring Cloud CircuitBreaker so a slow remote service does not exhaust your threads.
Going deeper
The default timeouts are often far too generous, or unlimited in older setups. Set connect and read timeouts explicitly for every client; an unbounded call is how one slow dependency takes everything down.
Because the call looks like a local method call, it is easy to forget it is a network call that can fail, be slow or be retried. Wrap callers in fallbacks, and do not call remote clients inside a database transaction.
Map remote error responses to domain exceptions in one place (the error decoder), not by checking status codes at every call site.
Under the hood
At startup an annotation registrar finds the interfaces and creates a JDK dynamic proxy for each. A call turns the method signature into a request template, runs the interceptors, executes the request with the underlying HTTP client (Apache, OkHttp or the JDK one) and decodes the body with the configured message converters.
Common mistakes
- No timeouts on the client.
- Retrying non-idempotent POST calls automatically.
- Calling a remote service inside a database transaction.
- Treating a remote call as cheap because it looks like a method call.
How Unravel recognises it
When you paste code, Unravel looks for these signs of declarative http clients (feign, http interfaces). Spotting them yourself is a good habit when you read unfamiliar code:
- Annotations:
@FeignClient@EnableFeignClients@HttpExchange@GetExchange@PostExchange@PutExchange@DeleteExchange - Types:
RequestInterceptorErrorDecoderHttpServiceProxyFactoryRestClientAdapterWebClientAdapterFeignRetryer
Part of a learning path
- Building and documenting APIs ยท previous: gRPC services and clients ยท next: Streaming responses and Server-Sent Events
Related concepts
Frequently asked questions
What is Declarative HTTP clients (Feign, HTTP interfaces)?
You write a Java interface describing a remote REST API with annotations, and the framework generates the HTTP client behind it. Calling a method sends the request and parses the response.
What are the most common mistakes with Declarative HTTP clients (Feign, HTTP interfaces)?
No timeouts on the client. Retrying non-idempotent POST calls automatically. Calling a remote service inside a database transaction. Treating a remote call as cheap because it looks like a method call.
How can I see Declarative HTTP clients (Feign, HTTP interfaces) in real code?
Paste your own Java into Unravel and it will point out where declarative http clients (feign, http interfaces) appears, explain how it works at the detail level you choose, and draw the flow of the code around it.