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:

  1. The interface is annotated @RegisterRestClient (triggers the Vauban BCE).

  2. The interface is exported by its module-info.java (at least for the module that contains the caller).

  3. At least one base URL is resolvable (baseUri attribute, <configKey>/mp-rest/url or <fqn>/mp-rest/url via 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

jakarta.ws.rs.ext.MessageBodyReader<T>

Deserialises a response body to T.

jakarta.ws.rs.ext.MessageBodyWriter<T>

Serialises a request body from T.

jakarta.ws.rs.client.ClientRequestFilter

Modifies the request before sending (headers, logging).

jakarta.ws.rs.client.ClientResponseFilter

Reads/modifies the response after reception.

org.eclipse.microprofile.rest.client.ext.ResponseExceptionMapper<E>

Converts an HTTP response into a typed exception (default: any status ≥ 400 → WebApplicationException).

jakarta.ws.rs.ext.ParamConverterProvider

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 a default method of the interface (static if the string is literal, dynamic if it is wrapped in braces).

  • @RegisterClientHeaders — attaches a ClientHeadersFactory that 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:

  1. Discover @RegisterRestClient interfaces (@Enhancement(types = Object.class, withAnnotations = RegisterRestClient.class)).

  2. Synthesise a @RestClient-qualified bean for each (@Synthesis).

  3. 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/get JAX-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 @RestClient qualifier.

  • MP Config configuration via <configKey>/mp-rest/* keys.

For the server side (publishing the interface), use Cassini.