A modular HTTP client toolkit for Java 17+ with fluent and declarative APIs, pluggable transports, and support for sync, async, and reactive applications.
Java has no shortage of HTTP clients. But each one forces a trade-off:
- JDK HttpClient - low-level, no serialization, no interceptors, no declarative API
- Spring WebClient / RestClient - optimized for Spring applications and programming models
- Quarkus REST Client - tightly aligned with Quarkus and JAX-RS-style declarative clients
- OkHttp / Apache HttpClient - transport only, you build everything else yourself
- Feign - declarative only, no fluent API, limited reactive support
Ark gives you one client model that survives framework changes, transport changes, and execution-model changes.
Ark separates the concerns that other clients bundle together:
| Concern | Ark's approach |
|---|---|
| How to build requests | Fluent API or declarative interfaces - your choice |
| How to send them | Pluggable transports - JDK, Reactor Netty, Vert.x, Apache |
| How to serialize | Pluggable serializers - Jackson, JSON-B, or your own |
| How to execute | Sync, async, Reactor, Mutiny, Vert.x Future - same API |
| Where to run | Spring Boot, Quarkus, or standalone - same code |
One mental model. Any stack. No lock-in.
- One client model across stacks - use Ark in Spring, Quarkus, and plain Java
- Fluent when you want control - explicit request composition with full access to headers, params, and body
- Declarative when you want contracts -
@RegisterArkClientwith Spring@HttpExchangeor JAX-RS annotations - Transport-agnostic - plug in JDK, Reactor Netty, Vert.x, or Apache HttpClient
- Execution-model aware - sync, async, Reactor, Mutiny, and Vert.x Future
- Production-ready features - TLS, retry, logging, typed exceptions, and per-client config
- Native-image friendly - designed to work well in GraalVM-based deployments
Ark supports multiple ways to define HTTP clients.
Use the fluent API when you want full control over request composition.
Ark client = ArkClient.builder()
.serializer(new JacksonSerializer(new ObjectMapper()))
.transport(new ArkJdkHttpTransport(HttpClient.newBuilder().build()))
.baseUrl("https://api.example.com")
.build();
User user = client.get("/users/1")
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.body(User.class);Define an interface with @RegisterArkClient and inject it directly:
@RegisterArkClient(baseUrl = "${api.users.url}")
@HttpExchange("/users")
public interface UserApi {
@GetExchange("/{id}")
User getUser(@PathVariable String id);
}// Spring
public UserController(UserApi userApi) { ... }
// Quarkus
@Inject UserApi userApi;Supports Spring @HttpExchange and JAX-RS @Path/@GET/@POST annotations.
@RegisterArkClient(baseUrl = "${api.users.url}")
@Path("/users")
@Produces("application/json")
public interface UserApi {
@GET
@Path("/{id}")
User getUser(@PathParam("id") String id);
}// Quarkus
@Inject UserApi userApi;| Attribute | Default | Description |
|---|---|---|
configKey |
"" |
Key for per-client config in application.properties |
baseUrl |
"" |
Base URL, supports ${property} placeholders |
httpVersion |
HTTP_2 |
HTTP/1.1 or HTTP/2 |
connectTimeout |
10 |
Connection timeout (seconds) |
readTimeout |
30 |
Read timeout (seconds) |
interceptors |
{} |
Interceptor classes (auto-detects Request/Response) |
- Java 17+
- Fluent HTTP API
- Declarative HTTP clients with Spring
@HttpExchangeor JAX-RS@Path/@GET @RegisterArkClientfor zero-boilerplate auto-registration and injection- Pluggable transports (JDK, Reactor Netty, Vert.x, Apache HttpClient 5)
- Pluggable serializers (Jackson, Jackson Classic, JSON-B)
- Dedicated sync, async, Reactor, Mutiny, and Vert.x APIs
- Type-safe per-client configuration (
ArkProperties/@ConfigMapping) - Per-client interceptors and default headers via config
- Retry with exponential backoff and jitter
- TLS/SSL support (Spring SSL Bundles, Quarkus TLS Registry)
- Trust-all SSL for development
- Request/response logging (
NONE,BASIC,HEADERS,BODY) - Typed exception hierarchy (400-504 mapped to specific exceptions)
- Per-request timeout support
- HTTP/2 by default
- Spring Boot (sync + WebFlux) and Quarkus integration
- GraalVM native image support
- Easy to test and mock
Ark provides dedicated entry points for different execution models while preserving a consistent client experience.
| Model | Client | Return Type |
|---|---|---|
| Sync | ArkClient |
T |
| Async | AsyncArkClient |
CompletableFuture<T> |
| Reactor | ReactorArkClient |
Mono<T> |
| Mutiny | MutinyArkClient |
Uni<T> |
| Vert.x | VertxArkClient |
Future<T> |
Same fluent API - only the return type changes:
User user = client
.get("/users/1")
.retrieve()
.body(User.class);
CompletableFuture<User> cf = asyncClient
.get("/users/1")
.retrieve()
.body(User.class);
Mono<User> mono = reactorClient
.get("/users/1")
.retrieve()
.body(User.class);
Uni<User> uni = mutinyClient
.get("/users/1")
.retrieve()
.body(User.class);
Future<User> future = vertxClient
.get("/users/1")
.retrieve()
.body(User.class);Import the BOM first:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>xyz.juandiii</groupId>
<artifactId>ark-bom</artifactId>
<version>1.0.5</version> <!-- ark-bom -->
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>Then choose the modules you need.
<dependencies>
<dependency>
<groupId>xyz.juandiii</groupId>
<artifactId>ark-core</artifactId>
</dependency>
<dependency>
<groupId>xyz.juandiii</groupId>
<artifactId>ark-jackson</artifactId>
</dependency>
<dependency>
<groupId>xyz.juandiii</groupId>
<artifactId>ark-transport-jdk</artifactId>
</dependency>
</dependencies>For async support:
<dependency>
<groupId>xyz.juandiii</groupId>
<artifactId>ark-async</artifactId>
</dependency>For Reactor support:
<dependency>
<groupId>xyz.juandiii</groupId>
<artifactId>ark-reactor</artifactId>
</dependency>
<dependency>
<groupId>xyz.juandiii</groupId>
<artifactId>ark-transport-reactor</artifactId>
</dependency>For Mutiny support:
<dependency>
<groupId>xyz.juandiii</groupId>
<artifactId>ark-mutiny</artifactId>
</dependency>
<dependency>
<groupId>xyz.juandiii</groupId>
<artifactId>ark-transport-vertx-mutiny</artifactId>
</dependency>For Vert.x Future support:
<dependency>
<groupId>xyz.juandiii</groupId>
<artifactId>ark-vertx</artifactId>
</dependency>
<dependency>
<groupId>xyz.juandiii</groupId>
<artifactId>ark-transport-vertx</artifactId>
</dependency>For Spring Boot:
<dependency>
<groupId>xyz.juandiii</groupId>
<artifactId>ark-spring-boot-starter</artifactId>
</dependency>For Spring WebFlux:
<dependency>
<groupId>xyz.juandiii</groupId>
<artifactId>ark-spring-boot-starter-webflux</artifactId>
</dependency>For Quarkus (Jackson):
<dependency>
<groupId>xyz.juandiii</groupId>
<artifactId>ark-quarkus-jackson</artifactId>
</dependency>Auto-configures JsonSerializer (Jackson 2.x), ArkClient.Builder (sync), and MutinyArkClient.Builder (reactive) as CDI beans.
Ark uses a bridge pattern.
The transport layer is a thin adapter around an already configured HTTP client.
Ark does not own connection pools, low-level HTTP tuning, or TLS setup. Those concerns stay in the underlying transport.
This makes Ark flexible by design.
Built-in transports include:
- JDK
HttpClient - Reactor Netty
- Vert.x Web Client
- Vert.x Mutiny Web Client
- Apache HttpClient 5
You can also provide your own transport implementation.
- Getting Started
- Sync Client
- Error Handling - typed exception hierarchy
- Async Client
- Reactor Client
- Mutiny Client
- Vert.x Client
- Transport Model - built-in and custom transports
- Serialization - Jackson, JSON-B, custom
- Logging - LoggingInterceptor + TransportLogger
- Retry & Backoff - automatic retry with exponential backoff
- Multipart Upload - file upload with binary fidelity
- Declarative Spring Clients
- Declarative JAX-RS Clients
- Spring Boot Integration - sync + WebFlux, config, TLS
- Quarkus Integration
- Quarkus Jackson Extension
- Testing
- Design Principles
- Keep transport explicit
- Keep serialization replaceable
- Support fluent and declarative styles
- Keep execution models separate
- Stay framework-friendly
- Prefer composition over lock-in
mvn clean install
mvn clean install -DskipTests
mvn testContributions are welcome!
Please read CONTRIBUTING.md for guidelines on commit conventions, PR labels, and the release process.
Apache 2.0