MicroProfile REST Client
Type-safe REST clients with @RegisterRestClient, async, and exception mapping.
# 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 Java templates
Modern Java Rules
Java 21+ defaults: records, sealed types, pattern matching, virtual threads, var.
Java Testing with JUnit 5
JUnit 5 + AssertJ + Mockito + Testcontainers — opinionated test conventions.
Java Virtual Threads & Concurrency
Project Loom virtual threads, structured concurrency, and modern concurrent patterns.
Java Build (Maven & Gradle)
Build conventions, dependency management, multi-module projects, and CI patterns.
Java Streams & Collections
Streams API, immutable collections, Collectors, and idiomatic data manipulation.