Of course! Here is a comprehensive guide on how to make POST requests in Java for RESTful APIs, covering the most common and modern approaches.

We'll use a free public API for our examples: JSONPlaceholder, which is a great tool for testing. We'll create a new "post" resource using its /posts endpoint.
The data we'll send to create a new post will be:
{: "foo",
"body": "bar",
"userId": 1
}
And the server will respond with the created post, including a new id.
Method 1: The Modern Standard - java.net.http (Java 11+)
This is the recommended, modern, and built-in way to make HTTP requests in Java. It's non-blocking by default, integrates well with other modern Java features, and doesn't require any external libraries.

Example: HttpClient
This is a complete, runnable example.
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
public class Java11PostRequest {
public static void main(String[] args) {
// 1. Define the URL and the JSON payload
String url = "https://jsonplaceholder.typicode.com/posts";
String jsonPayload = """
{
"title": "foo",
"body": "bar",
"userId": 1
}
""";
// 2. Create an HttpClient
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.connectTimeout(Duration.ofSeconds(10))
.build();
// 3. Create an HttpRequest
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("Content-Type", "application/json") // Set the content type
.header("Accept", "application/json") // Expect a JSON response
.POST(HttpRequest.BodyPublishers.ofString(jsonPayload))
.build();
// 4. Send the request and get the response (synchronously)
try {
System.out.println("Sending synchronous POST request...");
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
// 5. Process the response
System.out.println("Response Status Code: " + response.statusCode());
System.out.println("Response Body: " + response.body());
} catch (Exception e) {
e.printStackTrace();
}
// --- Example of an asynchronous request ---
System.out.println("\n------------------------------------");
System.out.println("Sending asynchronous POST request...");
CompletableFuture<HttpResponse<String>> futureResponse = client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
futureResponse.thenAccept(response -> {
System.out.println("Async Response Status Code: " + response.statusCode());
System.out.println("Async Response Body: " + response.body());
}).exceptionally(e -> {
System.err.println("Async request failed: " + e.getMessage());
return null;
});
// Wait for the async call to complete (in a real app, you'd handle this differently)
futureResponse.join();
}
}
Key Concepts:
HttpClient: The central object for sending requests. It's highly configurable (e.g., timeouts, version).HttpRequest: Represents the request itself. You set the method (GET,POST, etc.), URI, headers, and body.HttpRequest.BodyPublishers: Used to create the body of the request.ofString()is perfect for sending JSON strings.HttpResponse.BodyHandlers: Specifies how to handle the response body.ofString()collects it into aString.- Synchronous (
client.send): The thread blocks until the response is received. - Asynchronous (
client.sendAsync): Returns aCompletableFuture, allowing for non-blocking operations.
Method 2: The Classic - HttpURLConnection (Built-in, but Verbose)
Before Java 11, this was the standard way to do it with the standard library. It's more verbose and less flexible than HttpClient, but you don't need any dependencies.
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
public class HttpURLConnectionPostRequest {
public static void main(String[] args) {
String url = "https://jsonplaceholder.typicode.com/posts";
String jsonPayload = "{\"title\": \"foo\", \"body\": \"bar\", \"userId\": 1}";
try {
// 1. Create a URL object
URL urlObj = new URL(url);
HttpURLConnection connection = (HttpURLConnection) urlObj.openConnection();
// 2. Set the request method and headers
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("Accept", "application/json");
connection.setDoOutput(true); // This is crucial for POST requests
// 3. Write the request body
try (OutputStream os = connection.getOutputStream()) {
byte[] input = jsonPayload.getBytes(StandardCharsets.UTF_8);
os.write(input, 0, input.length);
}
// 4. Get the response code
int responseCode = connection.getResponseCode();
System.out.println("Response Code: " + responseCode);
// 5. Read the response (omitted for brevity, but involves BufferedReader)
// The logic for reading the response is more complex than with HttpClient.
} catch (Exception e) {
e.printStackTrace();
}
}
}
Key Concepts:
setRequestMethod("POST"): Explicitly sets the HTTP method.setDoOutput(true): This is the most important step. It tells the connection that you're going to write data to the server.getOutputStream(): Provides an output stream to write the request body.getResponseCode(): Retrieves the HTTP status code (e.g., 200, 201, 404).
Method 3: The Popular Library - Apache HttpClient (External Dependency)
This is a very powerful and mature library from the Apache Software Foundation. It's widely used in many enterprise applications and offers more features and sometimes better performance than the built-in options.
First, you need to add the dependency to your project.
For Maven (pom.xml):
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.3.1</version> <!-- Use the latest version -->
</dependency>
For Gradle (build.gradle):
implementation 'org.apache.httpcomponents.client5:httpclient5:5.3.1' // Use the latest version
Example: Apache HttpClient 5
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.entity.StringEntity;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.io.entity.EntityUtils;
public class ApacheHttpClientPostRequest {
public static void main(String[] args) {
String url = "https://jsonplaceholder.typicode.com/posts";
String jsonPayload = "{\"title\": \"foo\", \"body\": \"bar\", \"userId\": 1}";
// Use try-with-resources to ensure the client is closed
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
// 1. Create an HttpPost object
HttpPost httpPost = new HttpPost(url);
// 2. Set the request entity (the body)
StringEntity entity = new StringEntity(jsonPayload, ContentType.APPLICATION_JSON);
httpPost.setEntity(entity);
// 3. Execute the request and get the response
System.out.println("Executing request: " + httpPost.getMethod() + " " + httpPost.getUri());
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
// 4. Process the response
System.out.println("Response Status: " + response.getCode());
String responseBody = EntityUtils.toString(response.getEntity());
System.out.println("Response Body: " + responseBody);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Key Concepts:
CloseableHttpClient: The main client object. Using it in a try-with-resources block is a best practice.HttpPost: The class representing a POST request.StringEntity: Wraps your string payload and allows you to specify its content type.httpClient.execute(): Executes the request and returns aCloseableHttpResponse.EntityUtils.toString(): A convenient utility to read the response body into a string.
Summary and Comparison
| Feature | java.net.http (Java 11+) |
HttpURLConnection (Java 8-) |
Apache HttpClient 5 |
|---|---|---|---|
| Dependencies | None (Built-in) | None (Built-in) | Required (Maven/Gradle) |
| Ease of Use | Excellent. Clean, fluent API. | Poor. Verbose and clunky. | Good. Well-documented but more complex than java.net.http. |
| Performance | Excellent. Non-blocking by default. | Fair. Blocking by default. | Excellent. Highly configurable connection pooling. |
| Flexibility | High. Supports HTTP/1.1, HTTP/2, WebSockets. | Low. Limited features. | Very High. Extensive customization for headers, auth, retries, etc. |
| Recommendation | Recommended for all new projects on Java 11+. | Only for legacy projects or environments where you can't add dependencies. | A great choice for complex applications or if you're already using it in your project stack. |
For any new Java development targeting Java 11 or later, java.net.http is the clear winner. It's modern, powerful, and requires no external setup.
