This page defines the MicroProfile Rest Client vocabulary as Cyrano implements it. Implementation choices (proxy generation, transport, BCE) are described in Internals.
Typed client interface
A client interface is a Java interface annotated with the same JAX-RS constructs you would use server-side — @Path, @GET, @PathParam, @QueryParam, @Produces, @Consumes. It describes the remote service’s HTTP contract as if it were a local typed method.
Three conditions are enough for an interface to become a Cyrano client:
-
The interface is annotated
@RegisterRestClient(triggers the Vauban BCE). -
The interface is reachable by Cyrano: either it is compiled with
cyrano-processor(a strict Java module can then keep the client package fully encapsulated and only declareprovides ClientProxyFactory with …), or — for the runtime fallback — it is readable/exported toio.vidocq.cyrano.core. -
At least one base URL is resolvable (
baseUriattribute,<configKey>/mp-rest/urlor<fqn>/mp-rest/urlvia MP Config).
The proxy class is resolved by ClientProxyRegistry in three tiers: the APT-generated <Interface>$$CyranoClient (via ServiceLoader of ClientProxyFactory, then by naming convention), and only as a last resort a Cyrano$<SimpleName> class generated at runtime by the Class-File API and memoised by CyranoProxyCache.
RestClientBuilder
org.eclipse.microprofile.rest.client.RestClientBuilder is the standard entry point of the spec. Cyrano provides a CyranoRestClientBuilder implementation loaded through ServiceLoader (RestClientBuilderResolver SPI).
UsersClient client = RestClientBuilder.newBuilder()
.baseUri(URI.create("https://api.example.com"))
.connectTimeout(2, TimeUnit.SECONDS)
.readTimeout(5, TimeUnit.SECONDS)
.register(MyExceptionMapper.class)
.build(UsersClient.class);
The builder is usable in pure SE. Under CDI, it acts as the low-level mechanism; the cyrano-cdi-vauban BCE invokes it inside the SyntheticCreator behind @Inject @RestClient.
@RegisterRestClient
@RegisterRestClient (spec §6.1) marks an interface as a REST client to register in CDI. Two attributes:
-
baseUri— default URL, low priority. -
configKey— logical key under which MP Config exposes the URL and runtime options (<configKey>/mp-rest/url, etc.). High priority.
Without configKey, the interface’s FQN serves as the key (<interface.fqn>/mp-rest/url).
@RestClient qualifier
The qualifier org.eclipse.microprofile.rest.client.inject.RestClient distinguishes Cyrano-synthesised beans from any other beans that may implement the same interface. On the caller side:
@Inject
@RestClient
UsersClient client;
Without the qualifier, injection would resolve a possibly local bean implementing UsersClient — most likely not what you want.
Providers
A provider is a class registered on the client that intervenes in the send/receive pipeline. Cyrano supports the standard types from the spec:
| Provider | Role |
|---|---|
|
Deserialises a response body to |
|
Serialises a request body from |
|
Modifies the request before sending (headers, logging). |
|
Reads/modifies the response after reception. |
|
Converts an HTTP response into a typed exception (default: any status ≥ 400 → |
|
Converts a Java parameter value to/from its HTTP text form. |
Declarative registration: @RegisterProvider(MyMapper.class) or @RegisterProviders({A.class, B.class}) on the interface. Programmatic registration: RestClientBuilder.register(…).
Headers — static and dynamic
-
@HeaderParam("X-Foo")— the value comes from a method parameter. -
@ClientHeaderParam(name = "X-Trace", value = "{computeTrace}")— the value comes from adefaultmethod of the interface (static if the string is literal, dynamic if it is wrapped in braces). -
@RegisterClientHeaders— attaches aClientHeadersFactorythat can consult the inbound request (useful for server-to-client context propagation).
Async via CompletionStage<T>
A method whose return type is java.util.concurrent.CompletionStage<T> triggers a non-blocking send via HttpClient.sendAsync. The result is completed on a virtual thread.
@GET
CompletionStage<List<UserDto>> listAllAsync();
No platform pool to manage; no callback hell. The method chains naturally with thenApply, thenCompose, exceptionally.
Build Compatible Extensions (BCE)
CDI 4.1 replaced runtime Portable Extensions with a family of compile-time hooks. Cyrano’s CyranoRestClientCdiExtension uses exactly two of them — @Enhancement and @Synthesis (through Vauban):
-
Discover
@RegisterRestClientinterfaces (@Enhancement(types = Object.class, withAnnotations = RegisterRestClient.class, withSubtypes = true)) — a@RegisterRestClienton anything that is not an interface is reported through the BCEMessagesfacility. -
Synthesise a
@RestClient-qualified bean for each (@Synthesis), created byCyranoRestClientSyntheticCreator.
Everything is compile-time: no runtime Extension, no hot classpath scan.
Differences with the Jakarta REST Client (server side)
Cyrano is not a generic JAX-RS client (jakarta.ws.rs.client.Client). It implements the MicroProfile Rest Client profile which specialises the model:
-
Mandatory typed interface — no fluent
target/path/getJAX-RS-style API. -
Same JAX-RS annotations, but read-only (on the interface) instead of write-style (on an invoked method).
-
Native CDI integration via the
@RestClientqualifier. -
MP Config configuration via
<configKey>/mp-rest/*keys.
For the server side (publishing the interface), use Cassini.