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 exported by its
module-info.java(at least for the module that contains the caller). -
At least one base URL is resolvable (
baseUriattribute,<configKey>/mp-rest/urlor<fqn>/mp-rest/urlvia MP Config).
On first use, the interface receives a runtime artifact: a Cyrano$<SimpleName> class generated 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 compile-time hooks: @Discovery, @Enhancement, @Registration, @Synthesis, @Validation. Cyrano leverages them (through Vauban) to:
-
Discover
@RegisterRestClientinterfaces (@Enhancement(types = Object.class, withAnnotations = RegisterRestClient.class)). -
Synthesise a
@RestClient-qualified bean for each (@Synthesis). -
Validate that each client has at least one resolvable base URL (
@Validation).
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.