Specification overview for MicroProfile Rest Client

Last Updated:  October 11, 2020 | Published: September 3, 2019

In a distributed system your services usually communicate via HTTP and expose REST APIs. External clients or other services in your system consume these endpoints on a regular basis to e.g. fetch data from a different part of the domain. If you are using Java EE you can utilize the JAX-RS WebTarget and Client for this kind of communication. With the MicroProfile Rest Client specification, you'll get a more advanced and simpler way of creating these RESTful clients. You just declare interfaces and use a more declarative approach (like you might already know it from the Feign library).

Learn more about the MicroProfile Rest Client specification, its annotations, and how to use it in this blog post.

Specification profile: MicroProfile Rest Client

  • Current version: 1.4
  • GitHub repository
  • Latest specification document
  • Basic use case: Provide a type-safe approach to invoke RESTful services over HTTP.

Defining the RESTful client

For defining the Rest Client you just need a Java interface and model the remote REST API using JAX-RS annotations:

You can specify the response type with a specific POJO (JSON-B will then try to deserialize the HTTP response body) or use the generic Response class of JAX-RS.

Furthermore, you can indicate an asynchronous execution, if you use CompletionStage<T> as the method return type:

Path variables and query parameters for the remote endpoint can be specified with @PathParam and @QueryParam:

You can define the media type of the request and the expected media type of the response on either interface level or for each method separately:

If you have to declare specific HTTP headers (e.g. for authentication), you can pass them either to the method with @HeaderParam or define them with @ClientHeaderParam (static value or refer to a method):

Specifying multiple HTTP headers

If you want to generate multiple HTTP headers or propagate HTTP headers from an incoming JAX-RS request (e.g. pass the Authorization header to a downstream system), you can use the ClientHeadersFactory.

This interface specifies one method that returns the final HTTP headers for an outgoing client call. The headers might still be manipulated by a filter or any other mechanism before sending the client request.

While implementing this method you get two arguments passed to the update method. First, yo get passed incoming headers if the Rest Client is used as part of a JAX-RS request. These might be empty.
Furthermore, you have access to the HTTP headers you specified already at your interface while using e.g. @ClientHeaderParam:

Inside your implementation, you can now define logic for the outgoing HTTP headers. As an example I'm merging the incoming headers with the client outgoing headers and add three more headers manually:

Besides the benefit of propagating HTTP headers of a JAX-RS request, you can use @Inject here if your implementation is managed by CDI. This allows you to inject secrets for example or any other CDI bean to calculate a header value.

Finally, you have to register your factory implementation using @RegisterClientHeaders(NameOfFactoryImpl.class) on your Rest Client interface.

You can get further information on using the ClientHeadersFactory interface in the MicroProfile Rest Client 1.4 update video.

Using the client interface

Once you define your Rest Client interface you have two ways of using them. First, you can make use of the programmatic approach using the RestClientBuilder. With this builder we can set the base URI, define timeouts and register JAX-RS features/provider like ClientResponseFilter, MessageBodyReader, ReaderInterceptor etc.

In addition to this, we can use CDI to inject the Rest Client.  To register the interface as a CDI managed bean during runtime, the interface requires the @RegisterRestClient annotation:

With the @RegisterProvider you can register further JAX-RS provider and features as you've seen it in the programmatic approach. If you don't specify any scope for the interface,  the @Dependent scope will be used by default. With this scope, your Rest Client bean is bound (dependent) to the lifecycle of the injector class.

You can now use it as any other CDI bean and inject it into your classes. Make sure to add the CDI qualifier @RestClient to the injection point:

Further configuration for the Rest Client

If you use the CDI approach, you can make use of MicroProfile Config to further configure the Rest Client. You can specify the following properties with MicroProfile Config:

  • Base URL (.../mp-rest/url)
  • Base URI (.../mp-rest/uri)
  • The CDI scope of the client as a fully qualified class name (.../mp-rest/scope)
  • JAX-RS provider as a comma-separated list of fully qualified class names (../mp-rest/providers)
  • The priority of a registered provider (.../mp-rest/providers/com.acme.MyProvider/priority)
  • Connect and read timeouts (.../mp-rest/connectTimeout and .../mp-rest/readTimeout)

You can specify these properties for each client individually as you have to specify the fully qualified class name of  the Rest Client for each property:

YouTube video for using MicroProfile Rest Client

Watch the following YouTube video of my Getting started with MicroProfile series to see MicroProfile Rest Client in action:

You can find the source code with further instructions to run this example on GitHub.

Have fun using MicroProfile Rest Client,

Phil

  • Thank you for this article. I have a question about ClientHeadersFactory. I have implemented it and want to use another cdi bean to calculate Authorization header, but I have raised Nullpointer Exception, because injected bean is not initialized. Do you have any recommendations about it?

  • {"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}
    >