Next-Generation REST Client
juneau-rest-client (org.apache.juneau.rest.client.RestClient) is the recommended REST client. It is generally
available in 10.0.0, transport-agnostic, and uses the JDK HttpClient by default. juneau-rest-client-classic
(org.apache.juneau.rest.client.classic.RestClient, Apache HttpClient 4.5) stays supported and is not deprecated; it is
the stable choice for existing users. See Choosing a REST Client.
Juneau 10.0 promotes the next-generation REST client and HTTP type stack to the canonical names
under org.apache.juneau.rest.client.*, org.apache.juneau.rest.mock.*, and
org.apache.juneau.http.*. The pre-existing Apache HttpClient 4.5–based stack ships unchanged under
.classic packages and the new juneau-rest-client-classic Maven module — see
V10 Migration Guide for the import-rename table.
See the guide: Call an existing REST API with a typed proxy
The next-generation stack:
- Uses only JDK types in its API surface (org.apache.juneau.http). No
org.apache.http.*on any signature. - Introduces an
HttpTransportabstraction so you can plug in any HTTP client (Apache HC 4.5, Apache HC 5, JDKHttpClient, OkHttp, Jetty, or a custom transport) without changing user code. - Replaces 40+ Apache HC–specific builder methods on the classic
RestClient.Builderwith a small, focused builder. Transport-specific configuration lives on the transport builder, not onRestClient.Builder.
Package Layout
| Package | Module | Role |
|---|---|---|
org.apache.juneau.http | juneau-rest-common | JDK-native HTTP types (HttpHeader, HttpPart, HttpBody, status types, ~73 RFC-named headers, ~50 named response types, multipart, remote-proxy annotations). No httpcore dependency. |
org.apache.juneau.rest.client | juneau-rest-client | HttpTransport abstraction, canonical RestClient / RestRequest / RestResponse, interceptors, body converters, JUL-level-driven debug logging (RestClientDebugFormatter). No httpcore dependency. |
org.apache.juneau.rest.mock | juneau-rest-mock | MockHttpTransport and the canonical MockRestClient for in-process testing against a Juneau RestContext. |
org.apache.juneau.http.classic | juneau-rest-common-classic | Pre-existing Apache HttpCore-based types (~170 classes). Kept for the classic stack. |
org.apache.juneau.rest.client.classic | juneau-rest-client-classic (new) | Pre-existing RestClient that implements org.apache.http.client.HttpClient. Apache HttpClient 4.5–based. |
org.apache.juneau.rest.mock.classic | juneau-rest-mock | Pre-existing MockRestClient (extends classic RestClient and implements HttpClientConnection) plus its MockRestRequest / MockRestResponse / MockLogger / MockConsole / MockHttpClientConnectionManager helpers. |
Transport Modules
The JDK HttpClient–based JavaHttpTransport is built into the canonical juneau-rest-client
artifact and is the default — it requires no third-party dependencies on Java 11+.
To swap to a different native HTTP client, add one of the optional transport modules below. Each
ships only its own native client and registers a higher-priority HttpTransportProvider via
ServiceLoader, which automatically takes over discovery. An explicit
RestClient.builder().transport(...) call always wins.
| Module | Artifact ID | Native client |
|---|---|---|
| Apache HttpClient 4.5 | juneau-rest-client-apache-httpclient-45 | org.apache.httpcomponents:httpclient:4.5.x |
| Apache HttpClient 5 | juneau-rest-client-apache-httpclient-50 | org.apache.httpcomponents.client5:httpclient5 |
| OkHttp | juneau-rest-client-okhttp | com.squareup.okhttp3:okhttp-jvm |
| Eclipse Jetty client | juneau-rest-client-jetty | org.eclipse.jetty:jetty-client |
Each optional module ships three classes:
*Transport— theHttpTransportimplementation, with staticcreate()andbuilder()factories.*TransportBuilder— a final builder whosehttpClient(...)method accepts the native client, so all native-client–specific configuration (pooling, proxies, TLS, authentication) is done on that client's own builder (e.g.ApacheHc45Transport.builder().httpClient(HttpClients.custom()….build())).*TransportProvider—ServiceLoaderentry for auto-discovery.
See HTTP Transport Adapters for the full feature matrix,
JavaHttpTransport configuration, and per-transport authentication examples.
Quick Start
import org.apache.http.impl.client.*;
import org.apache.juneau.marshall.json5.*;
import org.apache.juneau.rest.client.*;
import org.apache.juneau.rest.client.apachehttpclient45.*;
// Pre-build the serializer/parser — configure marshalling externally
var serializer = Json5Serializer.create().build();
var parser = Json5Parser.create().build();
// Choose a transport explicitly and configure the underlying native client
HttpTransport transport = ApacheHc45Transport.builder()
.httpClient(HttpClients.custom().setMaxConnTotal(50).build())
.build();
RestClient client = RestClient.builder()
.transport(transport)
.serializer(serializer)
.parser(parser)
.rootUrl("https://api.example.com")
.build();
// Always use try-with-resources on the response — close() releases the connection
try (var resp = client.get("/users/{id}").pathData("id", 42).run()) {
var user = resp.body().as(User.class);
}
// At application shutdown
client.close();
Zero-Config Shortcut: create()
RestClient.create() is a zero-config shortcut — it's shorthand for RestClient.builder().build():
an auto-discovered transport, no default serializer/parser, and no root URL. Reach for it when you just
want a client to issue requests against absolute URLs with no marshalling configured up front:
RestClient client = RestClient.create();
try (var resp = client.get("https://api.example.com/users/{id}").pathData("id", 42).run()) {
var body = resp.body().asString();
}
Use RestClient.builder() — as shown above — whenever you need to configure a transport, serializer/
parser, root URL, default headers, interceptors, or any other builder option. create() and builder()
are the only two idioms for obtaining an NG RestClient; create() is not a legacy alias for
builder() (that shape belongs to the classic client,
which is why porting classic code replaces RestClient.create() with RestClient.builder() — see
Migration Path below).
Auto-Discovery
If you don't pass an explicit transport, RestClient uses ServiceLoader to pick the
highest-priority HttpTransportProvider on the classpath — this applies whether you configure the
client via create() or via builder(). The following configures marshalling explicitly while still
relying on transport auto-discovery:
RestClient client = RestClient.builder()
.serializer(Json5Serializer.DEFAULT)
.parser(Json5Parser.DEFAULT)
.rootUrl("https://api.example.com")
.build();
Key Design Differences from the Classic RestClient
| Concern | Classic RestClient (...client.classic) | Canonical RestClient (...client) |
|---|---|---|
| Client construction | create() returns a Builder (call .build() to get a client) | create() returns a built, ready-to-use client (the zero-config shortcut); builder() returns a Builder |
| HTTP integration | Implements org.apache.http.client.HttpClient; bound to Apache HC 4.5 | Composes an HttpTransport; transport-agnostic |
| Serialization model | Multi-language marshallers / format shortcuts (json(), xml(), …) | One pre-built serializer/parser pair per client; no multi-language mode |
| Builder surface | 40+ Apache HC passthrough methods | Small builder with only Juneau-specific concerns |
| Lifecycle | RestClient.close() | RestClient.close() — same idiom; both implement Closeable |
| Per-request lifecycle | RestRequest and RestResponse are both Closeable | Only RestResponse is Closeable; close() releases the connection |
| Header/part types | Implement org.apache.http.Header / NameValuePair | Implement JDK-only HttpHeader / HttpPart interfaces |
Schema validation on @Query/@Header/@FormData/@Path | Supported | Not supported — toString() + CollectionFormat only |
Resource Lifecycle
RestResponse — returned by RestRequest.run() — is the single Closeable in the user-facing
per-call API. Close it to release the underlying connection and stream:
// Correct — try-with-resources releases the connection even if run() or body() throws
try (var resp = client.get("/users").run()) {
return resp.body().as(UserList.class);
}
// At application shutdown
client.close();
RestRequest does not implement Closeable — it is a disposable fluent builder for a single
call, and run() transfers ownership of the connection to the returned RestResponse.
Typed Response Header Accessors
ResponseHeader (obtained via RestResponse.header(String)) has a small set of typed-wrapper
convenience methods, mirroring the classic stack's asXxxHeader() family without pulling in any
org.apache.http.classic.* types. Each is a thin parse over the header's raw string value using
JDK types or the transport-neutral types already in juneau-rest-common:
| Method | Return type | Absent / unparseable |
|---|---|---|
asBoolean() | Optional<Boolean> | empty |
asZonedDateTime() | Optional<ZonedDateTime> | empty (RFC-1123 HTTP date) |
asUri() | Optional<URI> | empty |
asEntityTag() | Optional<EntityTag> | empty (e.g. ETag) |
asEntityTags() | EntityTags (never null) | EntityTags.EMPTY (e.g. If-Match) |
asStringRanges() | StringRanges (never null) | empty (e.g. Accept-Encoding) |
try (var resp = client.get("/users/123").run()) {
Optional<ZonedDateTime> lastModified = resp.header("Last-Modified").asZonedDateTime();
Optional<EntityTag> etag = resp.header("ETag").asEntityTag();
EntityTags ifMatch = resp.header("If-Match").asEntityTags();
}
These sit alongside the existing generic accessors (asInteger(), asLong(), asOptional(),
asCsvList()); for anything not covered here, parse getValue() directly.
Declarative Remote-Proxy Features (Next-Gen)
The @Remote / @RemoteOp proxy annotations (and the @RemoteGet / @RemotePost / @RemotePut /
@RemoteDelete / @RemotePatch verb annotations) gain a family of declarative capabilities in the
next-gen engine, obtained via client.remote(MyProxy.class). These are documented on their own page,
NG Remote Proxies:
- Constant part values — always-applied headers, query and form data
- Content body defaults
- Per-part serializers
- Call policy — interceptors, timeouts, retries,
throwOnError - Dynamic URLs and base overrides —
@UrlandbaseUrl() - Declarative multipart —
@Multipart/@Part - Per-method format selection
- Streaming request and response bodies
MyProxy proxy = client.remote(MyProxy.class);
Multipart File Uploads
multipart/form-data is a first-class body type. Build a MultipartBody explicitly and send it
like any other request body via RestRequest.body(HttpBody):
var mp = MultipartBody.builder()
.field("note", "hello")
.file("attachment", new File("report.pdf"), "application/pdf")
.build();
try (var resp = client.post("/cases/123/attachments").body(mp).run()) {
resp.assertOk();
}
RestRequest.formData(...) continues to produce application/x-www-form-urlencoded and should
not be used for binary uploads. For a declarative alternative on remote proxies, see
Declarative Multipart.
Logging
Logging is framework-agnostic and driven by the JUL logger level. There is no .logger(...) builder
method and no RestLogger type; configure the debug logger name (and optionally a
RestClientDebugFormatter) on the builder, then raise the logger level:
RestClient client = RestClient.builder()
.serializer(Json5Serializer.DEFAULT)
.parser(Json5Parser.DEFAULT)
.debugLoggerName("myapp.http")
.build(); // uses the built-in JavaHttpTransport by default
// INFO = summary, FINE = + headers, FINEST = + bounded bodies (opt-in)
java.util.logging.Logger.getLogger("myapp.http").setLevel(java.util.logging.Level.FINE);
try (var resp = client.get("/users/123").run()) {
resp.assertOk();
}
Body dumping is off unless explicitly opted in, and credentials are redacted by default. See REST Client Logging and Debugging for the tier table, the secure-by-default rules, and request correlation.
Mock Transport (Serverless Testing)
MockHttpTransport routes TransportRequest directly to a Juneau RestContext without
network I/O. Use org.apache.juneau.rest.mock.MockRestClient for unit tests that exercise
serialization, remote proxies, interceptors, and assertions:
import org.apache.juneau.rest.mock.MockRestClient;
try (MockRestClient mock = MockRestClient.create(MyRestResource.class);
var resp = mock.get("/users/123").run()) {
var user = resp.body().as(User.class);
}
For the legacy fluent surface (.json5(), .plainText(), .disableRedirectHandling(), etc.)
use org.apache.juneau.rest.mock.classic.MockRestClient — see
Mock REST Client Overview.
Migration Path
The classic RestClient and its supporting juneau-rest-common Apache HttpCore types are
not deprecated in 10.0; they moved to .classic packages and the new
juneau-rest-client-classic module. Migration is opt-in and incremental:
- Stay on classic — keep using the same
RestClient/MockRestClienttypes you have today; just adjust imports as described in V10 Migration Guide and add a Maven dependency onjuneau-rest-client-classic. No behavioral change. - Adopt the canonical stack — add the NG transport module for your preferred HTTP stack
as a Maven dependency. Replace
RestClient.create()with the canonicalorg.apache.juneau.rest.client.RestClient.builder()and adapt builder calls — some map 1:1 (rootUrl, default headers/query, interceptors, logger);marshaller,errorCodes, andexecutorServicehave no NG equivalent — configure marshalling via the discreteserializer(...)/parser(...)builder methods instead. - Wrap each call's
RestResponsein try-with-resources. Callclose()on the client at application shutdown. - If you customized Apache HttpClient through
httpClientBuilder()/connectionManager()/defaultRequestConfig(), build the equivalent nativeCloseableHttpClientyourself and pass it to the transport builder'shttpClient(...)method (e.g.ApacheHc45Transport.builder().httpClient(...)). - If you relied on multi-language marshallers or per-request language overrides, create
one
RestClientper language.
The canonical stack will graduate to stable in a future release, at which point the classic stack may be deprecated and eventually removed.
See Also
- Call an existing REST API with a typed proxy: a guided walkthrough.
- Choosing a REST Client — classic vs. next-generation
- Deep dives: Requests, Responses, Body Converters, Assertions, Interceptors, Remote Proxies
- HTTP Transport Adapters — transport feature matrix and auth examples
- NG Client Security — SSRF guard, DNS pinning, redirect handling