Guide to Calling HTTP APIs with HttpClient
Updated at 1780840809000HttpClient is a synchronous HTTP client built on top of HttpURLConnection. Each request is represented by an object such as GetRequest, PostRequest, PutRequest, DeleteRequest, UploadRequest, or DownloadRequest, then passed to HttpClient for execution.In principle,
HttpClient opens a connection to the target URL, sets the HTTP method, timeout, and headers, serializes the request body based on Content-Type, reads the response, automatically decompresses gzip responses when the server returns Content-Encoding: gzip, and deserializes the response body based on the registered response type.If the status code is lower than
400, the body is returned normally. If the status code is 400 or higher, the client throws a corresponding exception such as HttpBadRequestException, HttpNotFoundException, HttpUnauthorizedException, or HttpRequestException for unrecognized HTTP error codes.By default, the client uses JSON for request and response bodies when
Content-Type is not specified. The default connect and read timeouts are both 15_000ms.
sequenceDiagram
participant App as Application
participant Client as HttpClient
participant Conn as HttpURLConnection
participant API as HTTP API
App->>Client: call(request) / request(request)
Client->>Conn: Open connection
Client->>Conn: Set method, headers, timeouts
Client->>Conn: Serialize request body if needed
Conn->>API: Send HTTP request
API-->>Conn: Return HTTP response
Conn-->>Client: Status, headers, body
Client->>Client: Decompress gzip if needed
Client->>Client: Deserialize body by response type
alt Status code < 400
Client-->>App: Return body or ResponseEntity
else Status code >= 400
Client-->>App: Throw HTTP exception
end
Creating HttpClient
HttpClient client = HttpClient.builder()
.connectTimeout(15_000)
.readTimeout(15_000)
.build();
You can also provide a custom
ObjectMapper or body converter:
HttpClient client = HttpClient.builder()
.objectMapper(objectMapper)
.addBodyConverter("application/custom", customConverter)
.build();
Calling an API and Returning the Body with call
Use
call(Request request) when you only need the response body. If the API returns an HTTP error, this method throws the corresponding exception.GetRequest request = new GetRequest() .setURL("http://localhost:8080/api/v1/users/1") .setResponseType(User.class); User user = client.call(request);
POST JSON example:
CreateUserRequest body = new CreateUserRequest("Alice", 20); PostRequest request = new PostRequest() .setURL("http://localhost:8080/api/v1/users") .setEntity(RequestEntity.body(body)) .setResponseType(User.class); User user = client.call(request);
Adding headers:
PostRequest request = new PostRequest() .setURL("http://localhost:8080/api/v1/users") .setEntity( RequestEntity.builder() .header("Authorization", "Bearer " + token) .body(body) .build() ) .setResponseType(User.class); User user = client.call(request);
Calling an API and Returning the Full Response with request
request(Request request) returns a ResponseEntity, including status code, headers, and body. Use this method when you need to inspect the status or response headers.GetRequest request = new GetRequest() .setURL("http://localhost:8080/api/v1/users") .setResponseType(User[].class); ResponseEntity response = client.request(request); int status = response.getStatus(); Object body = response.getBody();
Unlike
call, request does not automatically throw exceptions based on HTTP status. It returns the response as-is. If you want the same error-handling behavior as call, use getResponseBody.ResponseEntity response = client.request(request); User user = client.getResponseBody(response);
Low-Level API Call with request(HttpMethod, ...)
This method is useful when you want to pass the method, URL, entity, response type map, and timeout values directly without creating
GetRequest, PostRequest, and similar request objects.Map<Integer, Class<?>> responseTypes = new HashMap<>(); responseTypes.put(200, User.class); responseTypes.put(201, User.class); responseTypes.put(400, ErrorResponse.class); RequestEntity entity = RequestEntity.builder() .header("Authorization", "Bearer " + token) .body(body) .build(); ResponseEntity response = client.request( HttpMethod.POST, "http://localhost:8080/api/v1/users", entity, responseTypes, 15_000, 15_000 ); User user = client.getResponseBody(response);
GET API
GetRequest request = new GetRequest() .setURL("http://localhost:8080/api/v1/users?limit=10") .setResponseType(User[].class); User[] users = client.call(request);
GET requests do not send a body. If you need to send a token or other headers, set them through
RequestEntity:GetRequest request = new GetRequest() .setURL("http://localhost:8080/api/v1/profile") .setEntity( RequestEntity.builder() .header("Authorization", "Bearer " + token) .build() ) .setResponseType(Profile.class); Profile profile = client.call(request);
POST API
By default, the request body is sent as JSON.
PostRequest request = new PostRequest() .setURL("http://localhost:8080/api/v1/orders") .setEntity(RequestEntity.body(orderRequest)) .setResponseType(Order.class); Order order = client.call(request);
Sending form URL encoded data:
PostRequest request = new PostRequest() .setURL("http://localhost:8080/api/v1/login") .setEntity( RequestEntity.builder() .contentType(ContentTypes.APPLICATION_X_WWW_FORM_URLENCODED) .body(loginRequest) .build() ) .setResponseType(LoginResponse.class); LoginResponse response = client.call(request);
PUT API
PutRequest request = new PutRequest() .setURL("http://localhost:8080/api/v1/users/1") .setEntity(RequestEntity.body(updateUserRequest)) .setResponseType(User.class); User user = client.call(request);
DELETE API
DeleteRequest request = new DeleteRequest() .setURL("http://localhost:8080/api/v1/users/1") .setEntity( RequestEntity.builder() .header("Authorization", "Bearer " + token) .build() ) .setResponseType(String.class); String result = client.call(request);
Declaring Response Types by Status Code
By default,
setResponseType(Class<?>) applies to 200 OK. If the API can return multiple success statuses or has a specific error response body, you can register response types by status code.PostRequest request = new PostRequest() .setURL("http://localhost:8080/api/v1/users") .setEntity(RequestEntity.body(body)) .setResponseType(200, User.class) .setResponseType(201, User.class) .setResponseType(400, ErrorResponse.class); User user = client.call(request);
Uploading Files with callUpload
Use
callUpload(UploadRequest request) when uploading a file and you only need the response body. If the server returns an HTTP error, this method throws the corresponding exception.UploadRequest request = new UploadRequest() .setURL("http://localhost:8080/api/v1/files") .setFilePath("/tmp/avatar.png") .setFileName("avatar.png") .setResponseType(UploadResponse.class); UploadResponse response = client.callUpload(request);
Uploading from an
InputStream:try (InputStream inputStream = Files.newInputStream(Path.of("/tmp/avatar.png"))) { UploadRequest request = new UploadRequest() .setURL("http://localhost:8080/api/v1/files") .setInputStream(inputStream) .setFileName("avatar.png") .setResponseType(UploadResponse.class); UploadResponse response = client.callUpload(request); }
Uploading Files with upload
upload(UploadRequest request) returns a ResponseEntity, which is useful when you need to read the status or headers.UploadRequest request = new UploadRequest() .setURL("http://localhost:8080/api/v1/files") .setFilePath("/tmp/report.pdf") .setResponseType(UploadResponse.class); ResponseEntity response = client.upload(request);
You can use a cancellation token to cancel an upload in progress:
UploadCancellationToken token = new UploadCancellationToken(); UploadRequest request = new UploadRequest() .setURL("http://localhost:8080/api/v1/files") .setFilePath("/tmp/big-file.zip") .setResponseType(UploadResponse.class); UploadResponse response = client.callUpload(request, token);
Downloading a File to a Directory
download(String fileURL, File storeLocation) downloads a file to the specified directory and returns the saved file name. The file name is taken from Content-Disposition if the server returns that header; otherwise, it is extracted from the URL.
String fileName = client.download(
"http://localhost:8080/api/v1/files/report.pdf",
new File("/tmp/downloads")
);
Use
DownloadRequest when you need to add headers or configure timeouts:DownloadRequest request = new DownloadRequest() .setFileURL("http://localhost:8080/api/v1/files/report.pdf") .setHeaders( MultiValueMap.builder() .setValue("Authorization", "Bearer " + token) .build() ) .setConnectTimeout(15_000) .setReadTimeout(60_000); String fileName = client.download(request, new File("/tmp/downloads"));
Downloading to an OutputStream
Use this overload when you want to decide where the data is written, such as a response stream, buffer, or manually managed file stream.
try (OutputStream outputStream = Files.newOutputStream(Path.of("/tmp/report.pdf"))) { client.download( "http://localhost:8080/api/v1/files/report.pdf", outputStream ); }
Downloading with a New File Name
The
download(..., fileName) overload returns a DownloadFileResult, containing both the original file name and the new file name. If the original file has an extension, the client keeps that extension for the new file name.
DownloadFileResult result = client.download(
"http://localhost:8080/api/v1/files/report.pdf",
new File("/tmp/downloads"),
"monthly-report"
);
// originalFileName: report.pdf
// fileName: monthly-report.pdf
Cancelling a Download
DownloadCancellationToken token = new DownloadCancellationToken(); DownloadRequest request = new DownloadRequest() .setFileURL("http://localhost:8080/api/v1/files/big-file.zip"); String fileName = client.download( request, new File("/tmp/downloads"), token );
If the token is cancelled while downloading, the client throws
DownloadCancelledException.Error Handling
With
call and callUpload, status codes from 400 and above are converted into exceptions:try { User user = client.call(request); } catch (HttpNotFoundException e) { // 404 } catch (HttpUnauthorizedException e) { // 401 } catch (HttpRequestException e) { // Other HTTP errors }
With
request and upload, you receive a ResponseEntity first, then you can inspect the status manually or call getResponseBody.
ResponseEntity response = client.request(request);
if (response.getStatus() >= 400) {
client.getResponseBody(response);
}
Notes
If
Content-Type is not specified, the request body is serialized as JSON. If no response type is declared, the body is usually read as a string, and the client will try to parse it into a Map when possible.For production APIs, it is recommended to always call
setResponseType so the result is explicit and easier to control.