HTTP Client (Java 11+)
IntermediateJava 11's built-in HttpClient provides a modern, async-capable HTTP/1.1 and HTTP/2 client without needing external libraries.
Overview
Before Java 11, HttpURLConnection was the only built-in HTTP client — verbose and painful. Java 11 introduced java.net.http.HttpClient, supporting HTTP/1.1 and HTTP/2, both synchronous (send) and asynchronous (sendAsync returning CompletableFuture), WebSocket, and request/response body handlers for strings, bytes, streams, and files.
Synchronous Requests
Build an HttpClient (reusable — create once), construct an HttpRequest, then call send() with a BodyHandler that specifies how to parse the response body.
HttpClient is thread-safe and should be shared. HttpRequest is immutable and can be reused.
import java.net.http.*;
import java.net.URI;
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.connectTimeout(Duration.ofSeconds(10))
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users"))
.header("Accept", "application/json")
.header("Authorization", "Bearer " + token)
.GET()
.timeout(Duration.ofSeconds(30))
.build();
HttpResponse<String> response = client.send(
request, HttpResponse.BodyHandlers.ofString());
System.out.println("Status: " + response.statusCode());
System.out.println("Body: " + response.body());POST with JSON Body
For POST/PUT requests, provide a BodyPublisher. BodyPublishers.ofString() sends a string body. Set Content-Type header to match the body format.
String jsonBody = """
{
"name": "Alice",
"email": "alice@example.com"
}
""";
HttpRequest post = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
HttpResponse<String> response = client.send(
post, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 201) {
System.out.println("Created: " + response.body());
} else {
System.err.println("Error " + response.statusCode());
}Asynchronous Requests
sendAsync() returns a CompletableFuture<HttpResponse<T>> — the calling thread is not blocked. Chain thenApply / thenAccept for processing. Use allOf() to make multiple requests in parallel.
// Single async request
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(body -> System.out.println("Received: " + body))
.exceptionally(ex -> {
System.err.println("Failed: " + ex.getMessage());
return null;
});
// Parallel requests
List<URI> uris = List.of(
URI.create("https://api.example.com/users/1"),
URI.create("https://api.example.com/users/2"),
URI.create("https://api.example.com/users/3")
);
List<CompletableFuture<String>> futures = uris.stream()
.map(uri -> HttpRequest.newBuilder().uri(uri).build())
.map(req -> client.sendAsync(req, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body))
.collect(Collectors.toList());
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.thenRun(() -> futures.forEach(f -> System.out.println(f.join())));Key Points to Remember
- HttpClient is reusable and thread-safe — create once and share.
- send() blocks the calling thread; sendAsync() returns a CompletableFuture.
- BodyHandlers: ofString(), ofBytes(), ofFile(), ofInputStream() parse response body.
- BodyPublishers: ofString(), ofByteArray(), ofFile(), noBody() provide request body.
- HttpClient supports HTTP/2 multiplexing and automatic redirect following.
Practice HTTP Client (Java 11+) in the Playground
Run and modify code directly in your browser - no setup needed.
Interview Questions
Sign in to ask AriaWhat are the advantages of the Java 11 HttpClient over HttpURLConnection?
How do you make multiple HTTP requests in parallel using HttpClient?
What is the difference between send() and sendAsync()?
How would you implement retry logic with HttpClient?
How does HttpClient handle HTTP/2 connection multiplexing?
Ask Aria about HTTP Client (Java 11+)
Your personal AI tutor — ask anything about this concept