docs
reference
tessl install tessl/maven-io-quarkus--quarkus-resteasy-reactive@3.15.0A Jakarta REST implementation utilizing build time processing and Vert.x for high-performance REST endpoints with reactive capabilities in cloud-native environments.
Quarkus REST Client provides a type-safe, declarative way to invoke REST services. It combines standard Jakarta REST Client APIs with Quarkus-specific enhancements for reactive programming, custom headers, authentication, and flexible configuration.
import io.quarkus.rest.client.reactive.QuarkusRestClientBuilder;
import io.quarkus.rest.client.reactive.ClientExceptionMapper;
import io.quarkus.rest.client.reactive.ClientQueryParam;
import io.quarkus.rest.client.reactive.ClientQueryParams;
import io.quarkus.rest.client.reactive.ClientFormParam;
import io.quarkus.rest.client.reactive.ClientFormParams;
import io.quarkus.rest.client.reactive.ClientBasicAuth;
import io.quarkus.rest.client.reactive.ClientRedirectHandler;
import io.quarkus.rest.client.reactive.NotBody;
import io.quarkus.rest.client.reactive.HeaderFiller;
import io.quarkus.rest.client.reactive.ReactiveClientHeadersFactory;
import io.quarkus.rest.client.reactive.ComputedParamContext;
import org.jboss.resteasy.reactive.client.api.ClientMultipartForm;
import jakarta.ws.rs.*;
import io.smallrye.mutiny.Uni;Main entry point for building type-safe REST clients programmatically.
public interface QuarkusRestClientBuilder {
// Factory method
static QuarkusRestClientBuilder newBuilder();
// Base URL configuration
QuarkusRestClientBuilder baseUrl(URL url);
QuarkusRestClientBuilder baseUri(URI uri);
// Timeout configuration
QuarkusRestClientBuilder connectTimeout(long timeout, TimeUnit unit);
QuarkusRestClientBuilder readTimeout(long timeout, TimeUnit unit);
// TLS/SSL configuration
QuarkusRestClientBuilder tlsConfiguration(TlsConfiguration tlsConfiguration);
QuarkusRestClientBuilder sslContext(SSLContext sslContext);
QuarkusRestClientBuilder verifyHost(boolean verifyHost);
QuarkusRestClientBuilder trustStore(KeyStore trustStore);
QuarkusRestClientBuilder trustStore(KeyStore trustStore, String trustStorePassword);
QuarkusRestClientBuilder keyStore(KeyStore keyStore, String keystorePassword);
QuarkusRestClientBuilder hostnameVerifier(HostnameVerifier hostnameVerifier);
QuarkusRestClientBuilder trustAll(boolean trustAll);
// Proxy configuration
QuarkusRestClientBuilder proxyAddress(String proxyHost, int proxyPort);
QuarkusRestClientBuilder proxyUser(String proxyUser);
QuarkusRestClientBuilder proxyPassword(String proxyPassword);
QuarkusRestClientBuilder nonProxyHosts(String nonProxyHosts);
// HTTP client options
QuarkusRestClientBuilder followRedirects(boolean follow);
QuarkusRestClientBuilder multipartPostEncoderMode(String mode);
QuarkusRestClientBuilder queryParamStyle(QueryParamStyle style);
QuarkusRestClientBuilder userAgent(String userAgent);
// Headers and logging
QuarkusRestClientBuilder clientHeadersFactory(Class<? extends ClientHeadersFactory> clientHeadersFactoryClass);
QuarkusRestClientBuilder clientHeadersFactory(ClientHeadersFactory clientHeadersFactory);
QuarkusRestClientBuilder httpClientOptions(Class<? extends HttpClientOptions> httpClientOptionsClass);
QuarkusRestClientBuilder httpClientOptions(HttpClientOptions httpClientOptions);
QuarkusRestClientBuilder clientLogger(ClientLogger clientLogger);
QuarkusRestClientBuilder loggingScope(LoggingScope loggingScope);
QuarkusRestClientBuilder loggingBodyLimit(Integer limit);
// Build client
<T> T build(Class<T> clazz);
}Adds query parameters to client requests.
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(ClientQueryParams.class)
public @interface ClientQueryParam {
String name(); // Query parameter name
String[] value(); // Query parameter value(s)
boolean required() default true; // Whether parameter is required
}Container for multiple @ClientQueryParam annotations.
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ClientQueryParams {
ClientQueryParam[] value();
}Adds form parameters to client requests.
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(ClientFormParams.class)
public @interface ClientFormParam {
String name(); // Form parameter name
String[] value(); // Form parameter value(s)
boolean required() default true; // Whether parameter is required
}Container for multiple @ClientFormParam annotations.
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ClientFormParams {
ClientFormParam[] value();
}Configures HTTP Basic Authentication for client.
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ClientBasicAuth {
String username(); // Username
String password(); // Password
}Maps client-side HTTP errors to custom exceptions.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ClientExceptionMapper {
int priority() default Priorities.USER; // Mapper priority
}Custom redirect handling for client requests. Must be placed on a static method that returns a URI subclass.
Method Requirements:
static methodjava.net.URI (or subclass)jakarta.ws.rs.core.Response - The HTTP response triggering the redirect, ORjava.lang.reflect.Method - The client interface method being invoked@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ClientRedirectHandler {
int priority() default Priorities.USER; // Handler priority
}Marks a parameter as not being the request body.
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface NotBody {
}Abstract class for reactive header generation.
public abstract class ReactiveClientHeadersFactory extends ClientHeadersFactory {
/**
* Generate headers asynchronously
* @param incomingHeaders Headers from incoming request
* @param clientOutgoingHeaders Headers configured on client
* @return Uni with headers to add to outgoing request
*/
public abstract Uni<MultivaluedMap<String, String>> getHeaders(
MultivaluedMap<String, String> incomingHeaders,
MultivaluedMap<String, String> clientOutgoingHeaders
);
/**
* Blocking version throws exception (use getHeaders instead)
*/
@Override
public final MultivaluedMap<String, String> update(
MultivaluedMap<String, String> incomingHeaders,
MultivaluedMap<String, String> clientOutgoingHeaders
);
}Interface for adding headers to client requests.
public interface HeaderFiller {
void addHeaders(MultivaluedMap<String, String> headers);
}Extended version of HeaderFiller with access to the request context for more advanced header generation.
public interface ExtendedHeaderFiller extends HeaderFiller {
/**
* Add headers to the request with access to request context
* @param headers The header map to populate
* @param requestContext The client request context
*/
void addHeaders(
MultivaluedMap<String, String> headers,
ResteasyReactiveClientRequestContext requestContext
);
}Context for computed client parameters.
public interface ComputedParamContext {
String name(); // Parameter name
List<MethodParameter> methodParameters(); // Method parameters
interface MethodParameter {
Object value(); // Parameter value
}
}Interface for custom client request/response logging.
public interface ClientLogger {
void setBodySize(int bodySize);
void logResponse(HttpClientResponse response, boolean redirect);
void logRequest(HttpClientRequest request, Buffer body, boolean omitBody);
}Enum specifying the scope of client logging.
public enum LoggingScope {
NONE, // No logging
REQUEST_RESPONSE, // Log request and response metadata
ALL; // Log everything including bodies
}Enum from MicroProfile REST Client for query parameter encoding style.
// From org.eclipse.microprofile.rest.client.ext.QueryParamStyle
public enum QueryParamStyle {
MULTI_PAIRS, // Encode multi-valued params as separate name=value pairs
COMMA_SEPARATED, // Encode multi-valued params as name=value1,value2
ARRAY_PAIRS; // Encode multi-valued params as name[]=value1&name[]=value2
}Interface from Quarkus TLS registry for advanced TLS configuration. Pass to tlsConfiguration() method.
// From io.quarkus.tls.TlsConfiguration
public interface TlsConfiguration {
KeyStore getKeyStore();
KeyCertOptions getKeyStoreOptions();
KeyStore getTrustStore();
TrustOptions getTrustStoreOptions();
SSLOptions getSSLOptions();
SSLContext createSSLContext() throws Exception;
Optional<String> getHostnameVerificationAlgorithm();
boolean usesSni();
boolean isTrustAll();
boolean reload();
}Vert.x HTTP client options for advanced client configuration. Pass to httpClientOptions() method or implement a custom resolver.
Note: This is a Vert.x type (io.vertx.core.http.HttpClientOptions). Refer to Vert.x documentation for available options.
Client-side multipart form builder.
public abstract class ClientMultipartForm {
// Factory method
public static ClientMultipartForm create();
// Charset configuration
public ClientMultipartForm setCharset(String charset);
public ClientMultipartForm setCharset(Charset charset);
public Charset getCharset();
// Add form parts
public ClientMultipartForm attribute(String name, String value, String filename);
public ClientMultipartForm entity(String name, Object entity, String mediaType, Class<?> type);
public ClientMultipartForm entity(String name, String filename, Object entity, String mediaType, Class<?> type);
// File uploads
public ClientMultipartForm textFileUpload(String name, String filename, String pathname, String mediaType);
public ClientMultipartForm textFileUpload(String name, String filename, Buffer content, String mediaType);
public ClientMultipartForm stringFileUpload(String name, String filename, String content, String mediaType);
public ClientMultipartForm binaryFileUpload(String name, String filename, String pathname, String mediaType);
public ClientMultipartForm binaryFileUpload(String name, String filename, Buffer content, String mediaType);
public ClientMultipartForm multiAsBinaryFileUpload(String name, String filename, Multi<Byte> content, String mediaType);
public ClientMultipartForm multiAsTextFileUpload(String name, String filename, Multi<Byte> content, String mediaType);
public ClientMultipartForm fileUpload(FileUpload fileUpload);
}@Path("/api")
@RegisterRestClient(baseUri = "https://api.example.com")
public interface ApiClient {
@GET
@Path("/users/{id}")
Uni<User> getUser(@PathParam("id") String id);
@POST
@Path("/users")
Uni<User> createUser(User user);
@DELETE
@Path("/users/{id}")
Uni<Void> deleteUser(@PathParam("id") String id);
}
// Usage
@Inject
@RestClient
ApiClient apiClient;
public Uni<User> fetchUser(String id) {
return apiClient.getUser(id);
}ApiClient client = QuarkusRestClientBuilder.newBuilder()
.baseUri(URI.create("https://api.example.com"))
.connectTimeout(5, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build(ApiClient.class);
Uni<User> user = client.getUser("123");@Path("/api")
@RegisterRestClient
@ClientQueryParam(name = "api_key", value = "${api.key}")
public interface ApiClient {
@GET
@Path("/search")
@ClientQueryParam(name = "type", value = "user")
Uni<List<Result>> search(@QueryParam("q") String query);
}@Path("/api")
@RegisterRestClient
public interface AuthClient {
@POST
@Path("/login")
@ClientFormParam(name = "grant_type", value = "password")
Uni<Token> login(
@FormParam("username") String username,
@FormParam("password") String password
);
}@Path("/api")
@RegisterRestClient
@ClientBasicAuth(username = "${api.username}", password = "${api.password}")
public interface SecureApiClient {
@GET
@Path("/data")
Uni<Data> getData();
}@Path("/api")
@RegisterRestClient
public interface ApiClient {
@GET
@Path("/users/{id}")
Uni<User> getUser(@PathParam("id") String id);
@ClientExceptionMapper
static RuntimeException toException(Response response) {
if (response.getStatus() == 404) {
return new UserNotFoundException();
}
return new ApiException("HTTP " + response.getStatus());
}
}@ApplicationScoped
public class AuthHeaderFactory extends ReactiveClientHeadersFactory {
@Inject
TokenService tokenService;
@Override
public Uni<MultivaluedMap<String, String>> getHeaders(
MultivaluedMap<String, String> incomingHeaders,
MultivaluedMap<String, String> clientOutgoingHeaders
) {
return tokenService.getTokenAsync()
.map(token -> {
MultivaluedMap<String, String> headers = new MultivaluedHashMap<>();
headers.add("Authorization", "Bearer " + token);
return headers;
});
}
}
@Path("/api")
@RegisterRestClient
@RegisterProvider(AuthHeaderFactory.class)
public interface SecureApiClient {
@GET
@Path("/data")
Uni<Data> getData();
}@Path("/api")
@RegisterRestClient
public interface FileApiClient {
@POST
@Path("/upload")
@Consumes(MediaType.MULTIPART_FORM_DATA)
Uni<UploadResponse> upload(ClientMultipartForm form);
}
// Usage
ClientMultipartForm form = ClientMultipartForm.create()
.attribute("description", "My file", null)
.binaryFileUpload("file", "document.pdf", "/path/to/file.pdf", "application/pdf");
Uni<UploadResponse> response = fileApiClient.upload(form);KeyStore trustStore = KeyStore.getInstance("JKS");
trustStore.load(new FileInputStream("truststore.jks"), "password".toCharArray());
ApiClient client = QuarkusRestClientBuilder.newBuilder()
.baseUri(URI.create("https://secure-api.example.com"))
.trustStore(trustStore, "password")
.verifyHost(true)
.build(ApiClient.class);ApiClient client = QuarkusRestClientBuilder.newBuilder()
.baseUri(URI.create("https://api.example.com"))
.proxyAddress("proxy.corp.com", 8080)
.proxyUser("proxyuser")
.proxyPassword("proxypass")
.nonProxyHosts("localhost|*.internal")
.build(ApiClient.class);Using Response parameter (most common):
@Path("/api")
@RegisterRestClient
public interface ApiClient {
@GET
@Path("/resource")
Uni<Data> getData();
@ClientRedirectHandler
static URI redirect(Response response) {
// Custom redirect logic based on response
if (response.getStatus() == 301) {
String newLocation = response.getHeaderString("Location");
return URI.create(newLocation);
}
return null; // Use default handling
}
}Using Method parameter (for method-specific logic):
@Path("/api")
@RegisterRestClient
public interface ApiClient {
@GET
@Path("/resource")
Uni<Data> getData();
@ClientRedirectHandler
static URI redirect(Method method) {
// Custom redirect logic based on method
if (method.getName().equals("getData")) {
return URI.create("https://redirect.example.com/data");
}
return null; // Use default handling
}
}@Path("/api")
@RegisterRestClient
public interface ApiClient {
@POST
@Path("/upload")
Uni<Response> upload(
@NotBody @HeaderParam("X-File-Name") String filename,
byte[] fileContent // This is the body
);
}Configure REST clients via application.properties:
# Base configuration
quarkus.rest-client."io.example.ApiClient".url=https://api.example.com
quarkus.rest-client."io.example.ApiClient".scope=jakarta.enterprise.context.ApplicationScoped
# Timeouts
quarkus.rest-client."io.example.ApiClient".connect-timeout=5000
quarkus.rest-client."io.example.ApiClient".read-timeout=30000
# Authentication
quarkus.rest-client."io.example.ApiClient".username=user
quarkus.rest-client."io.example.ApiClient".password=pass
# TLS
quarkus.rest-client."io.example.ApiClient".trust-store=/path/to/truststore.jks
quarkus.rest-client."io.example.ApiClient".trust-store-password=password
quarkus.rest-client."io.example.ApiClient".verify-host=true
# Proxy
quarkus.rest-client."io.example.ApiClient".proxy-address=proxy.corp.com:8080
quarkus.rest-client."io.example.ApiClient".proxy-user=proxyuser
quarkus.rest-client."io.example.ApiClient".proxy-password=proxypass
quarkus.rest-client."io.example.ApiClient".non-proxy-hosts=localhost// Good: Non-blocking
@GET
Uni<User> getUser(@PathParam("id") String id);
// Avoid: Blocking
@GET
User getUser(@PathParam("id") String id);@ClientExceptionMapper
static RuntimeException mapException(Response response) {
return switch (response.getStatus()) {
case 404 -> new NotFoundException();
case 401 -> new UnauthorizedException();
case 403 -> new ForbiddenException();
default -> new ApiException("HTTP " + response.getStatus());
};
}// Good: Externalized configuration
@RegisterRestClient(configKey = "api-client")
// application.properties
// quarkus.rest-client.api-client.url=https://api.example.comQuarkusRestClientBuilder.newBuilder()
.connectTimeout(5, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build(ApiClient.class);