How to Send HTTP Requests in Java

September 29, 2023
Written by
Twilion
Twilion
Reviewed by
Twilion

Making HTTP requests is a core feature of modern programming, and is often one of the first things you want to do when learning a new programming language. For Java programmers there are many ways to do it, including core libraries in the JDK and third-party libraries.

In this article, you will learn about different ways to make HTTP requests from your Java code, as well as the updates and recommendations for Java core features and popular libraries that developers can use to make HTTP requests.

Prerequisites

  • Sign up for NASA's Astronomy Picture of the Day (APOD) API key. Check your email for the API key and remember to not share it with anyone. Note that in this post we are using DEMO_KEY which is provided by NASA to explore the API, but has low rate-limits and you are encouraged to create your own key. If you do this then substitute DEMO_KEY for your key in the samples below.
  • Java version 11 or later
  • A good Java IDE. I use Intellij IDEA but Eclipse and Netbeans are very capable too.

Reading the NASA APOD response data

The responses from the APOD API are in JSON format. We will be pulling a few fields out of each response so we need to parse the JSON into a Java object. Different HTTP libraries have different levels of support for JSON, but none of them knows about the format of APOD API responses so we have to define a class to match the format of the response ourselves. If you want more detail about this code please do read our post Three ways to use Jackson for JSON in Java.

Create a class named APOD.java with the following code to model the results of the API requests:

package com.twilio;

import com.fasterxml.jackson.annotation.JsonProperty;

public class APOD {
    public final String copyright;
    public final String date;
    public final String explanation;
    public final String hdUrl;
    public final String mediaType;
    public final String serviceVersion;
    public final String title;
    public final String url;

    public APOD(@JsonProperty("copyright") String copyright,
                @JsonProperty("date") String date,
                @JsonProperty("explanation") String explanation,
                @JsonProperty("hdurl") String hdUrl,
                @JsonProperty("media_type") String mediaType,
                @JsonProperty("service_version") String serviceVersion,
                @JsonProperty("title") String title,
                @JsonProperty("url") String url) {
        this.copyright = copyright;
        this.date = date;
        this.explanation = explanation;
        this.hdUrl = hdUrl;
        this.mediaType = mediaType;
        this.serviceVersion = serviceVersion;
        this.title = title;
        this.url = url;
    }
}

Note that this class depends on the Jackson library for the JsonProperty annotation. You will need to add that to your project - in the example repo we are using Maven so <dependency> is needed in pom.xml. This is in the example repo here.

Built-in Java Libraries

Back when everyone was stuck inside, forced to make HTTP requests for use cases such as searching for COVID-19 vaccines as well as retrieving pictures from NASA, Java developers would use libraries built into the Java platform. These are HttpUrlConnection (since Java 1.1)  and Java 11's HttpClient.

Java 1.1 HttpURLConnection

HttpURLConnection has no significant changes since it was added in 1997, so you can go ahead and make a GET request for the APOD data with the following code:

package com.twilio;

import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URI;

public class JavaHttpURLConnectionDemo {

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

        // Create a neat value object to hold the URL
        URL url = URI.create("https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY").toURL();

        // Open a connection(?) on the URL(?) and cast the response(??)
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();

        // Now it's "open", we can set the request method, headers etc.
        connection.setRequestProperty("accept", "application/json");

        // This line makes the request
        InputStream responseStream = connection.getInputStream();

        // Manually converting the response body InputStream to APOD using Jackson
        ObjectMapper mapper = new ObjectMapper();
        APOD apod = mapper.readValue(responseStream, APOD.class);

        // Finally we have the response
        System.out.println(apod.title);
    }
}

This is a reliable way to make requests if you are supporting clients that use older versions of Java and cannot add a dependency, but these days it's rarely chosen for a new project.

Note: HTTPUrlConnection also utilizes the ObjectMapper from the jackson-databind library in order to display the JSON details as we defined in the APOD.java class.

Check out the full demo code for the HttpURLConnection on this GitHub repo.

Java 11 HttpClient

Similar to the HttpURLConnection, the Java 11 HttpClient has not changed much since 2020 and remains a reliable method for making requests. This HttpClient was in development and preview for over a year before release so developers had plenty of chance to try it out and provide feedback. This meant that the teams working on Java could release HttpClient with a good degree of confidence that it would not need any significant redesign.

It is a much more modern and flexible way of making both asynchronous and synchronous HTTP requests than HttpURLConnection.

The client accepts a BodyHandler` that can convert an HTTP response into a class of your choosing. This can be handled synchronously or asynchronously.

To make asynchronous requests, use the code below to create a client and retrieve a response, once it's ready:

    private static void asynchronousRequest() throws InterruptedException, ExecutionException {

        // create a client
        var client = HttpClient.newHttpClient();

        // create a request
        var request = HttpRequest.newBuilder(
            URI.create("https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY"))
            .header("accept", "application/json")
            .build();

        // use the client to send the request
        var responseFuture = client.sendAsync(request, new JsonBodyHandler<>(APOD.class));

        // We can do other things here while the request is in-flight

        // This blocks until the request is complete
        var response = responseFuture.get();

        // the response:
        System.out.println(response.body().get().title);
    }

If your application needs it, you might consider running synchronous requests instead. The tradeoff here is that synchronous code is generally somewhat easier to understand as you can avoid thinking about multi-threading, but will probably use more system resources. This will be especially true if using Virtual Threads

To make this code synchronous change the last few lines from the asynchronousRequest function, as in the example below, so that a response is set right after a client is finished sending the request.

    private static void synchronousRequest() throws IOException, InterruptedException {
        // create a client
        var client = HttpClient.newHttpClient();

        // create a request
        var request = HttpRequest.newBuilder(
            URI.create("https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY"))
            .header("accept", "application/json")
            .build();

        // this line blocks until the response is completed so there is no way to do other processing while it is in flight
        HttpResponse<Supplier<APOD>> response = client.send(request, new JsonBodyHandler<>(APOD.class));

        // the response:
        System.out.println(response.body().get().title);
    }

Check out the full demo code for the HttpClient on this GitHub repo.

Check out third-party libraries

Third-party libraries might be your preference since they are helpful to maintain your app. For example, there is a community of developers out there, effectively, helping you debug problems you might encounter. As well as this, external libraries can provide different abstractions and levels of convenience for the coder - balancing this with performance and resource usage concerns. They very often have helpers for creating tests for your HTTP code, which makes a big difference to developer productivity.

Check out the following libraries and see if they fit your use case.

OkHttp

OkHttp is "Square’s meticulous HTTP client for Java and Kotiin", and has been a popular choice for a long time - 2023 marks its tenth birthday. It supports modern features like HTTP/2 and connection multiplexing, which can help improve the efficiency of your application.

To create an OkHTTPClient and make requests, use the code below:

package com.twilio;

import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

import java.io.IOException;

public class OkHTTPDemo {

    private static final ObjectMapper mapper = new ObjectMapper();
    public static void main(String[] args) throws IOException {

        OkHttpClient client = new OkHttpClient();

        Request request = new Request.Builder()
            .url("https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY")
            .build(); // defaults to GET
        Response response = client.newCall(request).execute();
        APOD apod = mapper.readValue(response.body().byteStream(), APOD.class);

        System.out.println(apod.title);
    }
}

Check out the full demo code for OkHttp on this GitHub repo.

Apache libraries

If you've been around long enough to use Commons HttpClient, well, update the naming convention because it's called HttpComponents Client now. Creative right? And something I have to look up every time I need it. Jokes about the naming aside, this is powerful and well-maintained and has a large user community - if you work on Java for any length of time I'm sure you will find a project that uses it.

Here is the code to try creating requests:

package com.twilio;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;

import java.io.IOException;

public class ApacheHttpClientDemo {
    private static final ObjectMapper mapper = new ObjectMapper();
    public static void main(String[] args) throws IOException {

        try (CloseableHttpClient client = HttpClients.createDefault()) {
            HttpGet request = new HttpGet("https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY");
            APOD response = client.execute(request, httpResponse ->
                mapper.readValue(httpResponse.getEntity().getContent(), APOD.class));
            System.out.println(response.title);

        }
    }
}

Check out the full demo code for the Apache Http Client on this GitHub repo.

Retrofit

Also developed by Square, Retrofit is a type-safe HTTP client for Android and Java that sits atop OkHTTP and provides an abstraction that works wonderfully with Java Interfaces, allowing standard testing tools for mocking and dependency injection. Converters can also be added to support the integration of other types of libraries. There are serialization libraries that automatically convert JSON responses into Java or Kotlin objects, and they can also serialize your request objects into JSON without manual handling. This is done using Jackson but you don't have to get your hands dirty - Retrofit handles it for you and you only need to deal with Java objects.

Here's the code for a demo using Retrofit:

package com.twilio;

import retrofit2.Retrofit;
import retrofit2.converter.jackson.JacksonConverterFactory;
import retrofit2.http.GET;
import retrofit2.http.Headers;
import retrofit2.http.Query;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class RetrofitDemo {

    public interface APODClient {
        @GET("/planetary/apod")
        @Headers("accept: application/json")
        CompletableFuture<APOD> getApod(@Query("api_key") String apiKey);
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("https://api.nasa.gov")
            .addConverterFactory(JacksonConverterFactory.create())
            .build();

        APODClient apodClient = retrofit.create(APODClient.class);

        CompletableFuture<APOD> response = apodClient.getApod("DEMO_KEY");

        // do other stuff here while the request is in-flight

        APOD apod = response.get();
        System.out.println(apod.title);
    }
}

Check out the full demo code for Retrofit on this GitHub repo.

The Spring Ecosystem

Last but certainly not least, if you're using Spring (via Boot or some other combination of its many modules) developers can use matching Spring-provided clients, with 2 options depending on whether you want a synchronous or asynchronous application.

Since these are part of the Spring ecosystem, the client integrates seamlessly with other ecosystem modules, such as dependency injection and transaction management.

In order to try out these demos, please follow this article to learn how to set up a Java Spring Boot application.

Spring RestTemplate

The Spring RestTemplate is considered legacy at this point and allows for synchronous calls. Still, we have included it because Spring's (deserved) popularity means it's likely that you could come across it in existing projects. RestTemplate is part of the spring-web module.

RestTemplate abstracts away much of the boilerplate code required to create and manage HTTP connections, headers, and payloads for quicker and more convenient requests. There is also convenience and flexibility in exception and error handling. This class can be mocked for unit testing, allowing you to test your code without making actual network requests.

You can extend RestTemplate by creating custom request and response interceptors, message converters, and other components to match the specific requirements of your application.

If you're building a traditional synchronous application with a blocking I/O model, RestTemplate might be more appropriate, however.

Get started with the RestTemplate by setting up a Java Spring Boot application. Then, create a service class in your project directory in a file named NasaApodService.java, and paste the following code into the file:

package com.twilio;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class NasaApodService {

    private final RestTemplate restTemplate;

    @Autowired
    public NasaApodService(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    public NasaApodResponse getNasaApod(String apiKey) {
        String apiUrl = "https://api.nasa.gov/planetary/apod?api_key=" + apiKey;
        ResponseEntity<NasaApodResponse> responseEntity = restTemplate.getForEntity(apiUrl, NasaApodResponse.class);
        return responseEntity.getBody();
    }
}

This service class creates a RestTemplate object to make a GET request to NASA's APOD API. The getNasaApod() method retrieves the response from the API, then maps it to a NasaApodResponse class.

Create the NasaApodResponse.java model class to store the structure of the JSON response with its respected getters and setters:

package com.twilio; 

public class NasaApodResponse {
    private String title;
    private String explanation;
    private String url;
}

In order to hit the API endpoint, create the file NasaApodController.java and paste in the following code:

package com.twilio; 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class NasaApodController {

    private final NasaApodService nasaApodService;

    @Autowired
    public NasaApodController(NasaApodService nasaApodService) {
        this.nasaApodService = nasaApodService;
    }

    @GetMapping("/nasa/apod")
    public NasaApodResponse getNasaApod(@RequestParam("api-key") String apiKey) {
        return nasaApodService.getNasaApod(apiKey);
    }
}

RestTemplate works nicely with other modules from Spring but it relies on a lower-level library for the actual HTTP work - in this example we're using HttpComponents for that. Please check the example repo for all the details. Once you have the code as it is in that repo you can run the main method in SpringHttpClientsDemoApplication then visit http://localhost:8080/nasa/apod?api-key=DEMO_KEY to see the result.

WebClient

On the other hand, the WebClient works asynchronously and is recommended for more modern applications. It is helpful, especially, when it comes to utilizing resources and improving overall throughput in an application that has multiple dependencies or microservices. It uses non-blocking operations and can therefore adapt to various reactive runtime environments, such as Reactor, RxJava, and CompletableFuture.  WebClient is part of the spring-webflux module.

WebClient supports reactive streams, making it possible to handle streaming data both in requests and responses. This is useful for scenarios like uploading or downloading large files. WebClient is well-suited for building highly scalable and responsive applications, especially in microservices architectures, and follows the latest architectural patterns.

Get started with the WebClient by setting up a Java Spring Boot application. Then, create a service class in your project directory with the file named NasaApodService.java, and paste the following code into the new file:

package com.twilio; 

import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

@Service
public class NasaApodService {

    private final WebClient webClient;

    public NasaApodService(WebClient.Builder webClientBuilder) {
        this.webClient = webClientBuilder.baseUrl("https://api.nasa.gov/planetary/apod").build();
    }

    public Mono<NasaApodResponse> getNasaApod(String apiKey) {
        return webClient.get()
                .uri(uriBuilder -> uriBuilder
                        .queryParam("api_key", apiKey)
                        .build())
                .retrieve()
                .bodyToMono(NasaApodResponse.class);
    }
}

This service class creates a WebClient object to make a GET request to NASA's APOD API. The getNasaApod() method retrieves the response from the API, then maps it to a NasaApodResponse class.

Create the model NasaApodResponse.java class to store the structure of the JSON response with its respected getters and setters, with the same code as in the RestTemplate example.

In order to hit the API endpoint, create the file NasaApodController.java and paste in the following code:

package com.twilio; 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

@RestController
public class NasaApodController {

    private final NasaApodService nasaApodService;

    @Autowired
    public NasaApodController(NasaApodService nasaApodService) {
        this.nasaApodService = nasaApodService;
    }

    @GetMapping("/nasa/apod")
    public Mono<NasaApodResponse> getNasaApod(@RequestParam("api-key") String apiKey) {
        return nasaApodService.getNasaApod(apiKey);
    }
}

As before you can test it by visiting http://localhost:8080/nasa/apod?api-key=DEMO_KEY in a browser after running the main method. Don't forget to insert your API key.

View the full code for the Spring WebClient demo at this GitHub repository.

What picture did you receive from the NASA API today?

Other HTTP clients for Java

There are plenty more ways to integrate an HTTP client into your application. Check out these other third party libraries:

  • REST Assured - an HTTP client designed for testing your REST services. It offers a fluent interface for making requests and helpful methods for making assertions about responses.
  • cvurl - a wrapper for the Java 11 HttpClient which rounds off some of the sharp edges you might encounter making complex requests.
  • Feign - Similar to Retrofit, Feign can build classes from annotated interfaces. Feign is highly flexible with multiple options for making and reading requests, metrics, retries, and more.
  • MicroProfile Rest Client - another client in the "build a class from an annotated interface" mode, this one is interesting because you can reuse the same interface to create a web server too, ensuring that the client and server match. If you're building a service and a client for that service, then it could be the one for you.

What's next?

Now that you know how to make HTTP requests using various methods in Java, why not try exploring other ways to make requests in different programming languages? Check out these articles below, and we'd love to hear from you if you find them useful:

Matthew is a Developer Evangelist at Twilio specialising in Java and other JVM languages. Previously he was a developer working on public cloud infrastructure, and before that a teacher of English as a foreign language.

Diane Phan is a software engineer on the Developer Voices team. She loves to help programmers tackle difficult challenges that might prevent them from bringing their projects to life. She can be reached at dphan [at] twilio.com or LinkedIn.