REST Client

A typical scenario in a Microservices architecture is the remote invocation of remote REST HTTP endpoints. Quarkus provides a typed REST client that follows the MicroProfile REST Client specification.

Let’s create a REST client that accesses https://swapi.info to get additional information about Movies. The endpoint we’re interested in is this one:

  • api/films/{id}, which returns specific info about the given movie.

{
	"title": "A New Hope",
	"episode_id": 4,
	"opening_crawl": "It is a period of civil war.\r\nRebel spaceships, striking\r\nfrom a hidden base, have won\r\ntheir first victory against\r\nthe evil Galactic Empire.\r\n\r\nDuring the battle, Rebel\r\nspies managed to steal secret\r\nplans to the Empire's\r\nultimate weapon, the DEATH\r\nSTAR, an armored space\r\nstation with enough power\r\nto destroy an entire planet.\r\n\r\nPursued by the Empire's\r\nsinister agents, Princess\r\nLeia races home aboard her\r\nstarship, custodian of the\r\nstolen plans that can save her\r\npeople and restore\r\nfreedom to the galaxy....",
	"director": "George Lucas",
	"producer": "Gary Kurtz, Rick McCallum",
	"release_date": "1977-05-25",
	"characters": [
		"https://swapi.info/api/people/1",
		"https://swapi.info/api/people/2",
		"https://swapi.info/api/people/3",
		"https://swapi.info/api/people/4",
		"https://swapi.info/api/people/5",
		"https://swapi.info/api/people/6",
		"https://swapi.info/api/people/7",
		"https://swapi.info/api/people/8",
		"https://swapi.info/api/people/9",
		"https://swapi.info/api/people/10",
		"https://swapi.info/api/people/12",
		"https://swapi.info/api/people/13",
		"https://swapi.info/api/people/14",
		"https://swapi.info/api/people/15",
		"https://swapi.info/api/people/16",
		"https://swapi.info/api/people/18",
		"https://swapi.info/api/people/19",
		"https://swapi.info/api/people/81"
	],
	"planets": [
		"https://swapi.info/api/planets/1",
		"https://swapi.info/api/planets/2",
		"https://swapi.info/api/planets/3"
	],
	"starships": [
		"https://swapi.info/api/starships/2",
		"https://swapi.info/api/starships/3",
		"https://swapi.info/api/starships/5",
		"https://swapi.info/api/starships/9",
		"https://swapi.info/api/starships/10",
		"https://swapi.info/api/starships/11",
		"https://swapi.info/api/starships/12",
		"https://swapi.info/api/starships/13"
	],
	"vehicles": [
		"https://swapi.info/api/vehicles/4",
		"https://swapi.info/api/vehicles/6",
		"https://swapi.info/api/vehicles/7",
		"https://swapi.info/api/vehicles/8"
	],
	"species": [
		"https://swapi.info/api/species/1",
		"https://swapi.info/api/species/2",
		"https://swapi.info/api/species/3",
		"https://swapi.info/api/species/4",
		"https://swapi.info/api/species/5"
	],
	"created": "2014-12-10T14:23:31.880000Z",
	"edited": "2014-12-20T19:49:45.256000Z",
	"url": "https://swapi.info/api/films/1"
}

Add the REST Client extension

Open a new terminal window, and make sure you’re at the root of your tutorial-app project, then run:

  • Maven

  • Quarkus CLI

./mvnw quarkus:add-extension -D"extensions=rest-client, rest-client-jackson"
quarkus extension add rest-client rest-client-jackson
[INFO] Scanning for projects...
[INFO]
[INFO] -----------------< com.redhat.developers:tutorial-app >-----------------
[INFO] Building tutorial-app 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- quarkus-maven-plugin:3.10.2:add-extension (default-cli) @ tutorial-app ---
✅ Adding extension io.quarkus:quarkus-rest-client-jackson
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.773 s
[INFO] Finished at: 2024-05-23T21:43:38-04:00
[INFO] ------------------------------------------------------------------------

Notice in the logs how Quarkus is reloading and the rest-client-jackson extension is now part of the Installed features.

Create Swapi POJO

We need to create a POJO object that is used to unmarshal a JSON message from http://swapi.dev.

Create a new Swapi Java class in src/main/java in the com.redhat.developers package with the following contents:

package com.redhat.developers;

public class Swapi {

    private String title;
    private int episode_id;
    private String opening_crawl;
    private String director;
    private String producer;


    public Swapi() {
    }

    public Swapi(String title, int episode_id, String opening_crawl, String director, String producer) {
        this.title = title;
        this.episode_id = episode_id;
        this.opening_crawl = opening_crawl;
        this.director = director;
        this.producer = producer;
    }


    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public int getEpisode_id() {
        return episode_id;
    }

    public void setEpisode_id(int episode_id) {
        this.episode_id = episode_id;
    }

    public String getOpening_crawl() {
        return opening_crawl;
    }

    public void setOpening_crawl(String opening_crawl) {
        this.opening_crawl = opening_crawl;
    }

    public String getDirector() {
        return director;
    }

    public void setDirector(String director) {
        this.director = director;
    }

    public String getProducer() {
        return producer;
    }

    public void setProducer(String producer) {
        this.producer = producer;
    }
}

Create SwapiService

Now we’re going to implement a Java interface that mimics the remote REST endpoint.

Create a new SwapiService Java interface in src/main/java in the com.redhat.developers package with the following contents:

package com.redhat.developers;


import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

@RegisterRestClient
public interface SwapiService {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path("/films/{id}")
    public Swapi getFilmById(@PathParam("id") String id);
}

Configure REST Client properties

Add the following properties to your application.properties in src/main/resources:

quarkus.rest-client."com.redhat.developers.SwapiService".url=https://swapi.info/api

Create MovieDTO

We’re going to enhance our MovieResource endpoint by creating a new MovieDTO POJO and add the additional information provided by the SwapiService.

Create a new MovieDTO Java class in src/main/java in the com.redhat.developers package with the following contents:

package com.redhat.developers;

import java.sql.Date;

public class MovieDTO {

    private String title;
    private Date releaseDate;
    private int episode_id;
    private String opening_crawl;
    private String director;
    private String producer;

    public MovieDTO() {
    }

    private MovieDTO(String title, Date releaseDate, int episode_id, String opening_crawl, String director, String producer) {
        this.title = title;
        this.releaseDate = releaseDate;
        this.episode_id = episode_id;
        this.opening_crawl = opening_crawl;
        this.director = director;
        this.producer = producer;
    }

    public static MovieDTO of(Movie movie, Swapi swapi) {
        return new MovieDTO(
                movie.title,
                movie.releaseDate,
                swapi.getEpisode_id(),
                swapi.getOpening_crawl(),
                swapi.getDirector(),
                swapi.getProducer()
        );
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public Date getReleaseDate() {
        return releaseDate;
    }

    public void setReleaseDate(Date releaseDate) {
        this.releaseDate = releaseDate;
    }

    public int getEpisode_id() {
        return episode_id;
    }

    public void setEpisode_id(int episode_id) {
        this.episode_id = episode_id;
    }

    public String getOpening_crawl() {
        return opening_crawl;
    }

    public void setOpening_crawl(String opening_crawl) {
        this.opening_crawl = opening_crawl;
    }

    public String getDirector() {
        return director;
    }

    public void setDirector(String director) {
        this.director = director;
    }

    public String getProducer() {
        return producer;
    }

    public void setProducer(String producer) {
        this.producer = producer;
    }
}

Change MovieResource to use SwapiService

Now that we have all the required classes, we can change MovieResource to get movies by title and use our SwapiService REST client via the @RestClient annotation.

Change the MovieResource Java class in src/main/java in the com.redhat.developers package with the following contents:

package com.redhat.developers;

import jakarta.transaction.Transactional;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.eclipse.microprofile.rest.client.inject.RestClient;

import java.util.List;
import java.util.stream.Collectors;

@Path("movie")
@Produces(MediaType.APPLICATION_JSON)
public class MovieResource {

    @RestClient
    SwapiService swapiService;

    @GET
    public List<MovieDTO> getMovie(@QueryParam("year") String year) {

        if (year != null) {
            return Movie.<Movie>findByYear(Integer.parseInt(year)).stream()
                    .map(movie -> MovieDTO.of(movie, swapiService.getFilmById(String.valueOf(movie.id))))
                    .collect(Collectors.toList());
        } else{
            return Movie.<Movie>listAll().stream()
                    .map(movie -> MovieDTO.of(movie, swapiService.getFilmById(String.valueOf(movie.id))))
                    .collect(Collectors.toList());
        }

    }

    @Transactional
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response newMovie(Movie movie) {
        movie.id = null;
        movie.persist();
        return Response.status(Response.Status.CREATED).entity(movie).build();
    }
}

Invoke the endpoint

You can check your new implementation using a REST client by pointing your browser to http://localhost:8080/movie?year=1980

You can also run the following command:

curl -w '\n' localhost:8080/movie?year=1980
[
  {
    "title": "The Empire Strikes Back",
    "releaseDate": "1980-05-17",
    "episode_id": 5,
    "opening_crawl": "It is a dark time for the\r\nRebellion. Although the Death\r\nStar has been destroyed,\r\nImperial troops have driven the\r\nRebel forces from their hidden\r\nbase and pursued them across\r\nthe galaxy.\r\n\r\nEvading the dreaded Imperial\r\nStarfleet, a group of freedom\r\nfighters led by Luke Skywalker\r\nhas established a new secret\r\nbase on the remote ice world\r\nof Hoth.\r\n\r\nThe evil lord Darth Vader,\r\nobsessed with finding young\r\nSkywalker, has dispatched\r\nthousands of remote probes into\r\nthe far reaches of space....",
    "director": "Irvin Kershner",
    "producer": "Gary Kurtz, Rick McCallum"
  }
]