GuicedEE REST Client
Annotation-driven REST client using the Vert.x 5 WebClient, fully managed by GuicedEE.
Core Concept
Declare an @Endpoint on any RestClient<Send, Receive> field, add @Named, and inject — URL, HTTP method, authentication, timeouts, and connection options are all driven from the annotation. Every call returns a Uni<Receive> for fully reactive composition.
Required Flow
- Add
com.guicedee:rest-clientdependency. - Configure
module-info.java:module my.app { requires com.guicedee.rest.client; opens my.app.clients to com.google.guice, com.guicedee.rest.client; opens my.app.dto to com.fasterxml.jackson.databind; } - Declare a REST client field with
@Endpoint:public class UserService { @Endpoint(url = "https://api.example.com/users", method = "POST", security = @EndpointSecurity(value = SecurityType.Bearer, token = "${API_TOKEN}")) @Named("create-user") private RestClient<CreateUserRequest, UserResponse> createUserClient; public Uni<UserResponse> createUser(CreateUserRequest request) { return createUserClient.send(request); } } - Once defined, inject the same client elsewhere by name only:
public class OrderService { @Inject @Named("create-user") private RestClient<CreateUserRequest, UserResponse> client; }
Service Registry Integration
When service-registry is on the classpath, URLs auto-resolve from the registry (no hard dependency):
// 1. Explicit registry: prefix — always resolves from registry
@Endpoint(url = "registry:jwebmp-website")
@Named("jwebmp") private RestClient<Void, Response> client;
// 2. Bare service name — if registered, uses registry URL
@Endpoint(url = "jwebmp-website")
@Named("jwebmp") private RestClient<Void, Response> client;
// 3. Full URL — passes through unchanged
@Endpoint(url = "https://api.example.com/v1")
@Named("example") private RestClient<Void, Response> client;
Resolution logic in RestClientRegistry.resolveUrl():
- Resolves
${ENV_VAR}placeholders first - If starts with
registry:→ resolves viaServiceRegistry.resolve() - If bare name (no
://, no/prefix) → tries registry, falls back to original value - Uses reflection — no compile-time dependency on service-registry module
Sending Requests
client.send(); // no body
client.send(body); // with body
client.send(body, headers, queryParams); // with headers and query params
client.publish(body); // fire-and-forget (no response)
All methods return Uni<Receive>.
Path Parameters
Use {paramName} placeholders in the endpoint URL:
@Endpoint(url = "https://api.example.com/users/{userId}", method = "GET")
@Named("get-user")
private RestClient<Void, UserResponse> getUserClient;
// Usage
getUserClient.pathParam("userId", "123").send();
Authentication
| Strategy | Annotation |
|---|---|
| Bearer/JWT | @EndpointSecurity(value = SecurityType.Bearer, token = "${API_TOKEN}") |
| Basic | @EndpointSecurity(value = SecurityType.Basic, username = "user", password = "${PASSWORD}") |
| API Key | @EndpointSecurity(value = SecurityType.ApiKey, apiKey = "${API_KEY}", apiKeyHeader = "X-API-Key") |
| OAuth2 | @EndpointSecurity(value = SecurityType.OAuth2, oauth2TokenUrl = "...", oauth2ClientId = "...", oauth2ClientSecret = "${SECRET}") |
| mTLS | @EndpointSecurity(value = SecurityType.ClientCert, clientCertPath = "...", clientCertPassword = "${CERT_PASS}") |
Package-Level Endpoints
Declare a shared base URL on package-info.java:
@Endpoint(url = "http://localhost:8080/api",
options = @EndpointOptions(readTimeout = 1000))
package com.example.api;
Field-level URLs starting with / are appended to the package-level base URL.
Environment Variable Overrides
Every annotation attribute can be overridden via REST_CLIENT_* environment variables without code changes.
Startup Flow
IGuiceContext.instance().inject()
└─ RestClientPreStartup (scans for @Endpoint-annotated fields)
└─ RestClientBinder (binds RestClient instances per @Named endpoint)
├─ RestClientRegistry (metadata: URL, types, security, options)
├─ WebClient cache (one per endpoint)
└─ RestClientConfigurator SPI (custom WebClientOptions)
Non-Negotiable Constraints
- Client packages must
openstocom.google.guiceandcom.guicedee.rest.client. - DTO packages must
openstocom.fasterxml.jackson.databind. - Module must
requires transitive com.guicedee.rest.client;. - Only one
@Endpointfield is needed per named endpoint; other classes inject by@Namedalone. RestClientConfiguratorSPI implementations must be dual-registered for tests.