BackendBench
Coffee

Unravel โ€บ Java concepts โ€บ APIs and web

Spring

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

Mental model

A restaurant menu with a phone number.

What happens, in order
  1. 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.
  2. 2Spring 6 has the same idea built in: an interface with @HttpExchange / @GetExchange methods and an HttpServiceProxyFactory backed by RestClient or WebClient
  3. 3Timeouts, headers (such as an auth token via RequestInterceptor) and error translation (ErrorDecoder) are configured once per client rather than in every call
  4. 4Combine with Resilience4j or Spring Cloud CircuitBreaker so a slow remote service does not exhaust your threads
Remember
Watch out
No timeouts on the clientRetrying non-idempotent POST calls automaticallyCalling a remote service inside a database transactionTreating a remote call as cheap because it looks like a method call
Example (highlighted lines explain it)
@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);}
  1. 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:

Part of a learning path

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.