DEV Community

Cover image for Java HTTP Requests Made Easy: A Practical Guide to java.net.http
Deividas Strole
Deividas Strole

Posted on

Java HTTP Requests Made Easy: A Practical Guide to java.net.http

If you're building a Java application that needs to communicate with a REST API, download data from a website, send JSON, or make HTTP requests, you don't necessarily need an external library.

Modern Java includes its own HTTP client in the java.net.http package.

In this tutorial, we'll learn how to use it to make:

  • GET requests
  • POST requests
  • Requests with headers
  • JSON requests
  • Asynchronous requests
  • Requests with query parameters
  • Requests with timeouts
  • Error handling

By the end, you'll have a reusable pattern for calling APIs directly from Java.


1. What is java.net.http?

The java.net.http package contains Java's built-in HTTP client API.

The three classes you'll use most often are:

HttpClient
HttpRequest
HttpResponse
Enter fullscreen mode Exit fullscreen mode

They represent three different parts of an HTTP operation:

HttpClient
    ↓
HttpRequest
    ↓
Server / API
    ↓
HttpResponse
Enter fullscreen mode Exit fullscreen mode

Think of them this way:

HttpClient   = sends requests
HttpRequest  = describes what you want to send
HttpResponse = contains what the server sends back
Enter fullscreen mode Exit fullscreen mode

The API supports both HTTP/1.1 and HTTP/2.


2. Your First GET Request

Let's start with the simplest possible example.

Suppose we want to request:

https://jsonplaceholder.typicode.com/posts/1
Enter fullscreen mode Exit fullscreen mode

We first create an HttpClient.

HttpClient client = HttpClient.newHttpClient();
Enter fullscreen mode Exit fullscreen mode

Then create an HttpRequest.

HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://jsonplaceholder.typicode.com/posts/1"))
        .GET()
        .build();
Enter fullscreen mode Exit fullscreen mode

Finally, send the request.

HttpResponse<String> response = client.send(
        request,
        HttpResponse.BodyHandlers.ofString()
);
Enter fullscreen mode Exit fullscreen mode

Here is the complete program:

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Main {

    public static void main(String[] args) throws Exception {

        HttpClient client = HttpClient.newHttpClient();

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(
                        "https://jsonplaceholder.typicode.com/posts/1"
                ))
                .GET()
                .build();

        HttpResponse<String> response = client.send(
                request,
                HttpResponse.BodyHandlers.ofString()
        );

        System.out.println(response.body());
    }
}
Enter fullscreen mode Exit fullscreen mode

The response should look similar to:

{
  "userId": 1,
  "id": 1,
  "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
  "body": "quia et suscipit..."
}
Enter fullscreen mode Exit fullscreen mode

Notice something important:

HttpResponse<String>
Enter fullscreen mode Exit fullscreen mode

We told Java that we want the response body converted into a String.

That happens because we used:

HttpResponse.BodyHandlers.ofString()
Enter fullscreen mode Exit fullscreen mode

3. Reading the HTTP Status Code

Usually, you don't want to blindly trust the body.

You should first check the HTTP status code.

System.out.println(response.statusCode());
Enter fullscreen mode Exit fullscreen mode

For example:

200
Enter fullscreen mode Exit fullscreen mode

means the request was successful.

Common HTTP codes include:

200  OK
201  Created
204  No Content

400  Bad Request
401  Unauthorized
403  Forbidden
404  Not Found

500  Internal Server Error
503  Service Unavailable
Enter fullscreen mode Exit fullscreen mode

You can handle the response like this:

if (response.statusCode() >= 200 &&
    response.statusCode() < 300) {

    System.out.println(response.body());

} else {

    System.out.println(
            "Request failed: " + response.statusCode()
    );
}
Enter fullscreen mode Exit fullscreen mode

This is a much safer pattern than assuming every HTTP request succeeds.


4. Adding Request Headers

Real APIs often require headers.

For example:

Accept: application/json
Authorization: Bearer TOKEN
User-Agent: MyJavaApplication
Enter fullscreen mode Exit fullscreen mode

You can add a header using:

.header()
Enter fullscreen mode Exit fullscreen mode

Example:

HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://example.com/api"))
        .header("Accept", "application/json")
        .header("User-Agent", "MyJavaApp")
        .GET()
        .build();
Enter fullscreen mode Exit fullscreen mode

You can also use:

.headers()
Enter fullscreen mode Exit fullscreen mode

Example:

HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://example.com/api"))
        .headers(
                "Accept", "application/json",
                "User-Agent", "MyJavaApp"
        )
        .GET()
        .build();
Enter fullscreen mode Exit fullscreen mode

5. Sending an Authorization Token

Many APIs use bearer-token authentication.

The HTTP header looks like:

Authorization: Bearer YOUR_TOKEN
Enter fullscreen mode Exit fullscreen mode

In Java:

String token = "YOUR_TOKEN";

HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://example.com/api/users"))
        .header(
                "Authorization",
                "Bearer " + token
        )
        .GET()
        .build();
Enter fullscreen mode Exit fullscreen mode

For production applications, avoid hardcoding secret API keys directly into source code.

Instead, use environment variables or another secret-management mechanism.

For example:

String token = System.getenv("API_TOKEN");
Enter fullscreen mode Exit fullscreen mode

6. Sending a POST Request

GET requests retrieve information.

POST requests usually send information.

Suppose we want to send this JSON:

{
  "title": "Learning Java HTTP Client",
  "body": "java.net.http is surprisingly convenient",
  "userId": 1
}
Enter fullscreen mode Exit fullscreen mode

First create the JSON as a string:

String json = """
        {
          "title": "Learning Java HTTP Client",
          "body": "java.net.http is surprisingly convenient",
          "userId": 1
        }
        """;
Enter fullscreen mode Exit fullscreen mode

Then construct the request:

HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create(
                "https://jsonplaceholder.typicode.com/posts"
        ))
        .header("Content-Type", "application/json")
        .POST(
                HttpRequest.BodyPublishers.ofString(json)
        )
        .build();
Enter fullscreen mode Exit fullscreen mode

Send it normally:

HttpResponse<String> response = client.send(
        request,
        HttpResponse.BodyHandlers.ofString()
);
Enter fullscreen mode Exit fullscreen mode

Complete example:

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class PostExample {

    public static void main(String[] args) throws Exception {

        HttpClient client = HttpClient.newHttpClient();

        String json = """
                {
                  "title": "Learning Java HTTP Client",
                  "body": "java.net.http is surprisingly convenient",
                  "userId": 1
                }
                """;

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(
                        "https://jsonplaceholder.typicode.com/posts"
                ))
                .header(
                        "Content-Type",
                        "application/json"
                )
                .POST(
                        HttpRequest.BodyPublishers.ofString(json)
                )
                .build();

        HttpResponse<String> response = client.send(
                request,
                HttpResponse.BodyHandlers.ofString()
        );

        System.out.println(
                "Status: " + response.statusCode()
        );

        System.out.println(response.body());
    }
}
Enter fullscreen mode Exit fullscreen mode

7. What Are BodyPublishers?

You'll notice this code:

HttpRequest.BodyPublishers.ofString(json)
Enter fullscreen mode Exit fullscreen mode

A BodyPublisher tells Java how to create the body of the HTTP request.

For a string:

HttpRequest.BodyPublishers.ofString("Hello")
Enter fullscreen mode Exit fullscreen mode

For no body:

HttpRequest.BodyPublishers.noBody()
Enter fullscreen mode Exit fullscreen mode

For a file:

HttpRequest.BodyPublishers.ofFile(path)
Enter fullscreen mode Exit fullscreen mode

For raw bytes:

HttpRequest.BodyPublishers.ofByteArray(bytes)
Enter fullscreen mode Exit fullscreen mode

A useful mental model is:

BodyPublisher = data going OUT
Enter fullscreen mode Exit fullscreen mode

8. What Are BodyHandlers?

A BodyHandler tells Java how you want to receive the server's response.

For text:

HttpResponse.BodyHandlers.ofString()
Enter fullscreen mode Exit fullscreen mode

For bytes:

HttpResponse.BodyHandlers.ofByteArray()
Enter fullscreen mode Exit fullscreen mode

To save directly to a file:

HttpResponse.BodyHandlers.ofFile(path)
Enter fullscreen mode Exit fullscreen mode

To ignore the body:

HttpResponse.BodyHandlers.discarding()
Enter fullscreen mode Exit fullscreen mode

A useful mental model is:

BodyPublisher = request body going OUT
BodyHandler   = response body coming IN
Enter fullscreen mode Exit fullscreen mode

For example:

HttpResponse<byte[]> response = client.send(
        request,
        HttpResponse.BodyHandlers.ofByteArray()
);
Enter fullscreen mode Exit fullscreen mode

Now:

response.body()
Enter fullscreen mode Exit fullscreen mode

returns:

byte[]
Enter fullscreen mode Exit fullscreen mode

instead of a String.


9. Downloading a File

The HTTP client can also download files.

For example:

Path destination = Path.of("image.jpg");

HttpResponse<Path> response = client.send(
        request,
        HttpResponse.BodyHandlers.ofFile(destination)
);
Enter fullscreen mode Exit fullscreen mode

Complete example:

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Path;

public class DownloadExample {

    public static void main(String[] args) throws Exception {

        HttpClient client = HttpClient.newHttpClient();

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(
                        "https://example.com/image.jpg"
                ))
                .GET()
                .build();

        Path file = Path.of("image.jpg");

        HttpResponse<Path> response = client.send(
                request,
                HttpResponse.BodyHandlers.ofFile(file)
        );

        System.out.println(
                "Downloaded to: " + response.body()
        );
    }
}
Enter fullscreen mode Exit fullscreen mode

10. Adding a Timeout

Network requests can hang or take longer than expected.

For that reason, you should usually configure timeouts.

You can configure a connection timeout when creating the client:

HttpClient client = HttpClient.newBuilder()
        .connectTimeout(Duration.ofSeconds(10))
        .build();
Enter fullscreen mode Exit fullscreen mode

And a request timeout:

HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://example.com"))
        .timeout(Duration.ofSeconds(20))
        .GET()
        .build();
Enter fullscreen mode Exit fullscreen mode

You will need:

import java.time.Duration;
Enter fullscreen mode Exit fullscreen mode

11. HTTP/2

Java's HTTP client supports HTTP/2.

You can explicitly request it:

HttpClient client = HttpClient.newBuilder()
        .version(HttpClient.Version.HTTP_2)
        .build();
Enter fullscreen mode Exit fullscreen mode

You can check which version was actually used:

System.out.println(response.version());
Enter fullscreen mode Exit fullscreen mode

Possible results include:

HTTP_1_1
HTTP_2
Enter fullscreen mode Exit fullscreen mode

12. Sending PUT Requests

PUT is commonly used for replacing or updating resources.

Example:

String json = """
        {
          "name": "John"
        }
        """;

HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create(
                "https://example.com/users/10"
        ))
        .header("Content-Type", "application/json")
        .PUT(
                HttpRequest.BodyPublishers.ofString(json)
        )
        .build();
Enter fullscreen mode Exit fullscreen mode

13. Sending DELETE Requests

DELETE is simple:

HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create(
                "https://example.com/users/10"
        ))
        .DELETE()
        .build();
Enter fullscreen mode Exit fullscreen mode

Then send it as usual:

HttpResponse<String> response = client.send(
        request,
        HttpResponse.BodyHandlers.ofString()
);
Enter fullscreen mode Exit fullscreen mode

14. Sending PATCH Requests

HttpRequest.Builder has convenient methods for:

GET
POST
PUT
DELETE
Enter fullscreen mode Exit fullscreen mode

But there isn't a dedicated .PATCH() method.

Use .method() instead:

String json = """
        {
          "email": "john@example.com"
        }
        """;

HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create(
                "https://example.com/users/10"
        ))
        .header("Content-Type", "application/json")
        .method(
                "PATCH",
                HttpRequest.BodyPublishers.ofString(json)
        )
        .build();
Enter fullscreen mode Exit fullscreen mode

15. GET Requests with Query Parameters

Imagine an API expects:

https://example.com/search?q=java&page=2
Enter fullscreen mode Exit fullscreen mode

A simple request is:

String url =
        "https://example.com/search?q=java&page=2";

HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create(url))
        .GET()
        .build();
Enter fullscreen mode Exit fullscreen mode

But values containing spaces or special characters should be URL-encoded.

For example:

String search = URLEncoder.encode(
        "Java HTTP Client",
        StandardCharsets.UTF_8
);
Enter fullscreen mode Exit fullscreen mode

Then:

String url =
        "https://example.com/search?q=" + search;
Enter fullscreen mode Exit fullscreen mode

You will need:

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
Enter fullscreen mode Exit fullscreen mode

16. Synchronous vs Asynchronous Requests

So far, we've been using:

client.send()
Enter fullscreen mode Exit fullscreen mode

This is synchronous.

That means Java waits until the server responds.

send request
     ↓
wait
     ↓
receive response
     ↓
continue program
Enter fullscreen mode Exit fullscreen mode

Example:

HttpResponse<String> response =
        client.send(
                request,
                HttpResponse.BodyHandlers.ofString()
        );
Enter fullscreen mode Exit fullscreen mode

Java also provides:

sendAsync()
Enter fullscreen mode Exit fullscreen mode

17. Making an Asynchronous Request

An asynchronous request looks like this:

client.sendAsync(
        request,
        HttpResponse.BodyHandlers.ofString()
);
Enter fullscreen mode Exit fullscreen mode

Instead of returning:

HttpResponse<String>
Enter fullscreen mode Exit fullscreen mode

it returns:

CompletableFuture<HttpResponse<String>>
Enter fullscreen mode Exit fullscreen mode

Example:

CompletableFuture<HttpResponse<String>> future =
        client.sendAsync(
                request,
                HttpResponse.BodyHandlers.ofString()
        );
Enter fullscreen mode Exit fullscreen mode

You can process the response when it becomes available:

future.thenAccept(response -> {
    System.out.println(response.statusCode());
    System.out.println(response.body());
});
Enter fullscreen mode Exit fullscreen mode

Complete example:

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class AsyncExample {

    public static void main(String[] args) {

        HttpClient client =
                HttpClient.newHttpClient();

        HttpRequest request =
                HttpRequest.newBuilder()
                        .uri(URI.create(
                                "https://jsonplaceholder.typicode.com/posts/1"
                        ))
                        .GET()
                        .build();

        client.sendAsync(
                request,
                HttpResponse.BodyHandlers.ofString()
        ).thenAccept(response -> {

            System.out.println(
                    response.statusCode()
            );

            System.out.println(
                    response.body()
            );

        }).join();
    }
}
Enter fullscreen mode Exit fullscreen mode

The .join() keeps this small demo program alive until the asynchronous operation finishes.


18. Why Use sendAsync()?

Imagine you need data from three APIs.

With synchronous requests:

Request A ──────── finish
                   Request B ─────── finish
                                       Request C ───── finish
Enter fullscreen mode Exit fullscreen mode

The total time can roughly become:

A + B + C
Enter fullscreen mode Exit fullscreen mode

With asynchronous requests, they can run concurrently:

Request A ──────────
Request B ──────
Request C ─────────────
Enter fullscreen mode Exit fullscreen mode

Example:

var request1 = HttpRequest.newBuilder()
        .uri(URI.create(
                "https://jsonplaceholder.typicode.com/posts/1"
        ))
        .GET()
        .build();

var request2 = HttpRequest.newBuilder()
        .uri(URI.create(
                "https://jsonplaceholder.typicode.com/posts/2"
        ))
        .GET()
        .build();

var future1 = client.sendAsync(
        request1,
        HttpResponse.BodyHandlers.ofString()
);

var future2 = client.sendAsync(
        request2,
        HttpResponse.BodyHandlers.ofString()
);

var response1 = future1.join();
var response2 = future2.join();

System.out.println(response1.body());
System.out.println(response2.body());
Enter fullscreen mode Exit fullscreen mode

19. Reading Response Headers

The HTTP response contains more than just a body.

You can get all headers with:

response.headers()
Enter fullscreen mode Exit fullscreen mode

Example:

System.out.println(
        response.headers().map()
);
Enter fullscreen mode Exit fullscreen mode

You can retrieve one specific header:

response.headers()
        .firstValue("content-type")
        .ifPresent(System.out::println);
Enter fullscreen mode Exit fullscreen mode

firstValue() returns an Optional because the header may not exist.


20. Handling Exceptions Properly

Earlier examples used:

throws Exception
Enter fullscreen mode Exit fullscreen mode

That's convenient for tutorials, but production code should handle exceptions more carefully.

client.send() can throw:

IOException
InterruptedException
Enter fullscreen mode Exit fullscreen mode

A better example:

try {

    HttpResponse<String> response =
            client.send(
                    request,
                    HttpResponse.BodyHandlers.ofString()
            );

    System.out.println(response.body());

} catch (IOException e) {

    System.err.println(
            "Network error: " + e.getMessage()
    );

} catch (InterruptedException e) {

    Thread.currentThread().interrupt();

    System.err.println(
            "Request was interrupted"
    );
}
Enter fullscreen mode Exit fullscreen mode

Calling:

Thread.currentThread().interrupt();
Enter fullscreen mode Exit fullscreen mode

restores the thread's interrupted status.


21. Network Failure Is Different from HTTP Failure

This distinction is important.

Suppose a server returns:

404 Not Found
Enter fullscreen mode Exit fullscreen mode

Java does not normally throw an exception just because the server returned a 404.

You still receive an:

HttpResponse
Enter fullscreen mode Exit fullscreen mode

and:

response.statusCode()
Enter fullscreen mode Exit fullscreen mode

might return:

404
Enter fullscreen mode Exit fullscreen mode

Exceptions are more likely for things such as:

DNS failure
connection failure
network interruption
timeout
Enter fullscreen mode Exit fullscreen mode

So your application should handle both HTTP failures and network failures.

try {

    HttpResponse<String> response =
            client.send(
                    request,
                    HttpResponse.BodyHandlers.ofString()
            );

    if (response.statusCode() >= 200 &&
        response.statusCode() < 300) {

        System.out.println(response.body());

    } else {

        System.err.println(
                "HTTP error: " +
                response.statusCode()
        );
    }

} catch (IOException e) {

    System.err.println(
            "Network error: " + e.getMessage()
    );

} catch (InterruptedException e) {

    Thread.currentThread().interrupt();
}
Enter fullscreen mode Exit fullscreen mode

22. Creating a Reusable API Method

Once you start making multiple requests, repeating the same code becomes annoying.

You can create a reusable method:

public static String get(String url)
        throws IOException, InterruptedException {

    HttpClient client =
            HttpClient.newHttpClient();

    HttpRequest request =
            HttpRequest.newBuilder()
                    .uri(URI.create(url))
                    .GET()
                    .build();

    HttpResponse<String> response =
            client.send(
                    request,
                    HttpResponse.BodyHandlers.ofString()
            );

    if (response.statusCode() >= 200 &&
        response.statusCode() < 300) {

        return response.body();
    }

    throw new IOException(
            "HTTP error: " +
            response.statusCode()
    );
}
Enter fullscreen mode Exit fullscreen mode

Then calling an API becomes:

String result = get(
        "https://jsonplaceholder.typicode.com/posts/1"
);

System.out.println(result);
Enter fullscreen mode Exit fullscreen mode

23. Reuse HttpClient

There's one improvement we should make to the previous example.

Instead of creating a new HttpClient for every request, you should generally reuse one.

public class ApiClient {

    private final HttpClient client;

    public ApiClient() {

        this.client = HttpClient.newBuilder()
                .connectTimeout(
                        Duration.ofSeconds(10)
                )
                .build();
    }

    public String get(String url)
            throws IOException, InterruptedException {

        HttpRequest request =
                HttpRequest.newBuilder()
                        .uri(URI.create(url))
                        .timeout(
                                Duration.ofSeconds(20)
                        )
                        .GET()
                        .build();

        HttpResponse<String> response =
                client.send(
                        request,
                        HttpResponse.BodyHandlers.ofString()
                );

        if (response.statusCode() >= 200 &&
            response.statusCode() < 300) {

            return response.body();
        }

        throw new IOException(
                "HTTP " +
                response.statusCode() +
                ": " +
                response.body()
        );
    }
}
Enter fullscreen mode Exit fullscreen mode

Usage:

ApiClient api = new ApiClient();

String response = api.get(
        "https://jsonplaceholder.typicode.com/posts/1"
);

System.out.println(response);
Enter fullscreen mode Exit fullscreen mode

This is closer to how HTTP code is usually structured in a real application.


24. But What About JSON?

One thing java.net.http does not try to be is a complete JSON library.

Suppose an API returns:

{
  "id": 1,
  "name": "John"
}
Enter fullscreen mode Exit fullscreen mode

java.net.http can retrieve it:

String json = response.body();
Enter fullscreen mode Exit fullscreen mode

But converting that JSON into:

User user;
Enter fullscreen mode Exit fullscreen mode

is normally handled by another library.

Popular choices include:

  • Jackson
  • Gson
  • JSON-B

A common architecture looks like this:

java.net.http
       ↓
HTTP communication
       ↓
JSON String
       ↓
Jackson / Gson
       ↓
Java object
Enter fullscreen mode Exit fullscreen mode

For example, with Jackson:

String json = response.body();

User user = objectMapper.readValue(
        json,
        User.class
);
Enter fullscreen mode Exit fullscreen mode

The HTTP client handles networking.

The JSON library handles serialization and deserialization.


25. A More Realistic REST API Client

Putting everything together:

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

public class RestClient {

    private final HttpClient client;

    public RestClient() {

        client = HttpClient.newBuilder()
                .connectTimeout(
                        Duration.ofSeconds(10)
                )
                .build();
    }

    public String get(String url)
            throws IOException, InterruptedException {

        HttpRequest request =
                HttpRequest.newBuilder()
                        .uri(URI.create(url))
                        .timeout(
                                Duration.ofSeconds(20)
                        )
                        .header(
                                "Accept",
                                "application/json"
                        )
                        .GET()
                        .build();

        return send(request);
    }

    public String post(
            String url,
            String json
    ) throws IOException, InterruptedException {

        HttpRequest request =
                HttpRequest.newBuilder()
                        .uri(URI.create(url))
                        .timeout(
                                Duration.ofSeconds(20)
                        )
                        .header(
                                "Accept",
                                "application/json"
                        )
                        .header(
                                "Content-Type",
                                "application/json"
                        )
                        .POST(
                                HttpRequest.BodyPublishers
                                        .ofString(json)
                        )
                        .build();

        return send(request);
    }

    private String send(HttpRequest request)
            throws IOException, InterruptedException {

        HttpResponse<String> response =
                client.send(
                        request,
                        HttpResponse.BodyHandlers.ofString()
                );

        int status = response.statusCode();

        if (status >= 200 && status < 300) {
            return response.body();
        }

        throw new IOException(
                "HTTP " +
                status +
                ": " +
                response.body()
        );
    }
}
Enter fullscreen mode Exit fullscreen mode

Now your application code becomes very simple:

RestClient client = new RestClient();

String result = client.get(
        "https://jsonplaceholder.typicode.com/posts/1"
);

System.out.println(result);
Enter fullscreen mode Exit fullscreen mode

Or:

String json = """
        {
          "title": "Hello",
          "body": "Java HTTP Client",
          "userId": 1
        }
        """;

String result = client.post(
        "https://jsonplaceholder.typicode.com/posts",
        json
);

System.out.println(result);
Enter fullscreen mode Exit fullscreen mode

26. Quick Cheat Sheet

Create a client

HttpClient client =
        HttpClient.newHttpClient();
Enter fullscreen mode Exit fullscreen mode

GET

HttpRequest request =
        HttpRequest.newBuilder()
                .uri(URI.create(url))
                .GET()
                .build();
Enter fullscreen mode Exit fullscreen mode

POST

HttpRequest request =
        HttpRequest.newBuilder()
                .uri(URI.create(url))
                .POST(
                        HttpRequest.BodyPublishers
                                .ofString(body)
                )
                .build();
Enter fullscreen mode Exit fullscreen mode

PUT

.PUT(
    HttpRequest.BodyPublishers.ofString(body)
)
Enter fullscreen mode Exit fullscreen mode

DELETE

.DELETE()
Enter fullscreen mode Exit fullscreen mode

PATCH

.method(
    "PATCH",
    HttpRequest.BodyPublishers.ofString(body)
)
Enter fullscreen mode Exit fullscreen mode

Add a header

.header(
    "Content-Type",
    "application/json"
)
Enter fullscreen mode Exit fullscreen mode

Send synchronously

HttpResponse<String> response =
        client.send(
                request,
                HttpResponse.BodyHandlers.ofString()
        );
Enter fullscreen mode Exit fullscreen mode

Send asynchronously

client.sendAsync(
        request,
        HttpResponse.BodyHandlers.ofString()
);
Enter fullscreen mode Exit fullscreen mode

Status code

response.statusCode()
Enter fullscreen mode Exit fullscreen mode

Response body

response.body()
Enter fullscreen mode Exit fullscreen mode

Response headers

response.headers()
Enter fullscreen mode Exit fullscreen mode

27. The Mental Model

If you remember nothing else from this tutorial, remember this pattern:

HttpClient client = HttpClient.newHttpClient();

HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create(url))
        .GET()
        .build();

HttpResponse<String> response = client.send(
        request,
        HttpResponse.BodyHandlers.ofString()
);

System.out.println(response.body());
Enter fullscreen mode Exit fullscreen mode

Or visually:

1. Create HttpClient
        ↓
2. Build HttpRequest
        ↓
3. client.send(...)
        ↓
4. Receive HttpResponse
        ↓
5. Check statusCode()
        ↓
6. Read body()
Enter fullscreen mode Exit fullscreen mode

Once that pattern makes sense, most of the API becomes easy to understand.


Final Thoughts

Java's built-in HTTP client is easy to overlook because developers often immediately reach for external dependencies.

But for many applications, java.net.http gives you everything you need:

  • HTTP/1.1
  • HTTP/2
  • GET
  • POST
  • PUT
  • DELETE
  • PATCH
  • Headers
  • Authentication headers
  • Request bodies
  • File downloads
  • Timeouts
  • Synchronous requests
  • Asynchronous requests

For simple REST API clients, scripts, desktop applications, microservices, and backend integrations, it can be more than enough.

The main thing it does not replace is a JSON serialization library such as Jackson or Gson.

A useful separation of responsibilities is:

java.net.http
    → talks to the server

Jackson / Gson
    → translates JSON

Your application
    → handles business logic
Enter fullscreen mode Exit fullscreen mode

And that's often all you need to build a clean Java API client.


If you found this useful, the next logical step is combining java.net.http + Jackson to automatically convert API responses into Java records and objects.


About the Author

Deividas Strole is a Full-Stack Developer based in California, specializing in Java, Spring Boot, JavaScript, React, SQL, and AI-powered applications. He writes about software engineering, modern full-stack development, and digital marketing.

Connect with me:

Top comments (0)