All MicroProfile templates

MicroProfile REST Client

Type-safe REST clients with @RegisterRestClient, async, and exception mapping.

DevZone Tools1,080 copiesUpdated Mar 17, 2026MicroProfileJakarta EEJava
# CLAUDE.md — MicroProfile REST Client

## Type-safe clients

- Define an interface annotated with `@RegisterRestClient`:
  ```java
  @RegisterRestClient(configKey = "stripe-api")
  @Path("/v1")
  public interface StripeApi {
      @POST @Path("/charges") @Consumes(JSON) @Produces(JSON)
      ChargeResponse charge(ChargeRequest req);
  }
  ```
- Inject as `@RestClient StripeApi stripe`.
- Same JAX-RS annotations as resources — `@Path`, `@GET`, `@POST`, `@Header`, `@QueryParam`.

## Configuration

- Configure base URL by `configKey`:
  ```
  stripe-api/mp-rest/url=https://api.stripe.com
  stripe-api/mp-rest/connectTimeout=2000
  stripe-api/mp-rest/readTimeout=5000
  ```
- Override per environment via standard Config sources.
- Don't hardcode URLs in `@RegisterRestClient(baseUri = "...")`. Use `configKey` and override via config.

## Headers

- Static headers via `@ClientHeaderParam`:
  ```java
  @ClientHeaderParam(name = "Authorization", value = "Bearer ${stripe.api.key}")
  public interface StripeApi { ... }
  ```
- Dynamic headers via `@HeaderParam` on a method parameter, or via a `ClientHeadersFactory` for cross-cutting (request id, tracing).

## Async

- Return `CompletionStage<T>` for non-blocking calls:
  ```java
  CompletionStage<ChargeResponse> chargeAsync(ChargeRequest req);
  ```
- Pair with `@Asynchronous` (MicroProfile Fault Tolerance) for thread-pool isolation.
- For reactive stacks (Quarkus + Mutiny), return `Uni<T>` instead.

## Error handling

- 4xx/5xx responses throw `WebApplicationException` by default. Catch and translate to domain exceptions.
- Custom exception mapping via `ResponseExceptionMapper`:
  ```java
  public class StripeErrorMapper implements ResponseExceptionMapper<StripeException> {
      public StripeException toThrowable(Response r) {
          return new StripeException(r.readEntity(StripeError.class));
      }
  }
  ```
- Register via `@RegisterProvider(StripeErrorMapper.class)` on the client interface.

## Filtering / interceptors

- `@RegisterProvider(MyClientFilter.class)` on the interface.
- Use for: request id propagation, retry hooks, request/response logging.
- Avoid logging full bodies in production filters — sanitize or sample.

## Resilience

- Combine with **MicroProfile Fault Tolerance**:
  ```java
  @Retry(maxRetries = 3, delay = 200)
  @Timeout(value = 5, unit = ChronoUnit.SECONDS)
  @CircuitBreaker(requestVolumeThreshold = 4)
  ChargeResponse charge(ChargeRequest req);
  ```
- Timeout < downstream's read timeout. Otherwise the client gives up before the server responds.

## Testing

- For unit tests, mock the interface as you would any CDI bean.
- For integration tests, use **WireMock** or **MockServer** to stand up a fake HTTP target.
- Don't hit real third-party APIs in tests. Sandbox/mock environments only.

## Don't

- Don't share a single client interface across many unrelated services. One client per upstream.
- Don't put auth tokens in `@ClientHeaderParam(value = "Bearer xxx")` literals — use config.
- Don't return `Response` from REST Client methods. Type-safe deserialization is the point.
- Don't create a new client instance per call. Inject — the runtime caches.

Other MicroProfile templates