Combine GraphQL with Java to build a flexible and modern API

Avoid under-fetching and over-fetching data when retrieving data using REST APIs.


In the past few years, developers have used RESTful web services over HTTP(s) to expose business functions using an API. The REST API uses server-driven fixed data responses, which means a developer (client) can't determine the result of a response. Instead, the server sends all the data back to the client, which is called over-fetching. The developer (client) needs to invoke multiple REST APIs after the first call until the client gets the required data, which results in under-fetching.

To create new microservices, developers using these REST APIs have been looking for ways to minimize over-fetching and under-fetching when retrieving data along with business logic.

GraphQL provides a client-driven query language and runtime to prevent this overhead on the client side and instead retrieve the exact data that the REST API requires. When GraphQL came out, many developers thought that it could replace existing REST API specifications. However, it's not a replacement but an alternative. This article explains how to consume GraphQL services using Quarkus applications. Quarkus is a Kubernetes-based framework for writing Java applications. If you haven't created a Quarkus application before, please read Writing Java with Quarkus in VS Code before continuing.

Add GraphQL extensions to your Quarkus project

Start by adding a Quarkus extension, smallrye-graphql, to an existing Quarkus Maven project by entering the following in a terminal (or you can use a Quarkus tool in Visual Studio Code):

$ mvn quarkus:add-extension -Dextensions="graphql"

You should see the following in the terminal:



✅ Extension io.quarkus:quarkus-smallrye-graphql has been installed
[INFO] ---------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ---------------------------------------------------------------------
[INFO] Total time:  9.833 s
[INFO] Finished at: 2020-07-29T22:00:28-04:00
[INFO] ---------------------------------------------------------------------


Add an entity and a service for GraphQL's API

Create two entity Java classes—for this example, which queries data on movies. call them Film and Hero—to represent GraphQL schemas that are a set of possible data (e.g., objects, fields, relationships) that an end user can retrieve:



public class Film {
 private String title;
 private Integer episodeID;
 private String director;
 private LocalDate releaseDate;

 // Getter & Setter methods here
   ...
}


public class Hero {


 private String name;
 private String surname;
 private Double height;
 private Integer mass;
 private Boolean darkSide;
 private LightSaber lightSaber;
 private List<Integer> episodeIds = new ArrayList<>();

 // Getter & Setter methods here
   ...
}

enum LightSaber {
    RED, BLUE, GREEN
}

Create a GraphQL API with CDI bean

Implement a few methods to retrieve the Film and Hero data with parameters in the CDI (Contexts and Dependency Injection) bean class (e.g., GalaxyService.java). Generate some example data:



public GalaxyService() {

   Film aNewHope = new Film();
   aNewHope.setTitle("A New Hope");
   aNewHope.setReleaseDate(LocalDate.of(1977, Month.MAY, 25));
   aNewHope.setEpisodeID(4);
   aNewHope.setDirector("George Lucas");
   films.add(aNewHope);
   ...

 Hero luke = new Hero();
   luke.setName("Luke");
   luke.setSurname("Skywalker");
   luke.setHeight(1.7);
   luke.setMass(73);
   luke.setLightSaber(LightSaber.GREEN);
   luke.setDarkSide(false);
   luke.getEpisodeIds().addAll(Arrays.asList(4, 5, 6));
   heroes.add(luke);
   ...

public List<Film> getAllFilms() {
 return films;
}

Now, create a GraphQL API class (e.g., FilmResource.java) to inject the CDI bean:



@GraphQLApi
public class FilmResource {

    @Inject
    GalaxyService service;

    @Query("allFilms")
    @Description("Get all Films from a galaxy far far away")
 public List<Film> getAllFilms() {
 return service.getAllFilms();
 }
}

The @GraphQLApi annotation enables you to use the CDI bean (e.g., GalaxyService) for a GraphQL endpoint. @Query annotation allows you to make the method (e.g., getAllFilms) queryable with a specific name (e.g., allFilms).

Access GraphiQL's UI, a GraphQL interface

The Quarkus smallrye-graphql extension enables you to use a GraphiQL tool for easy interaction with your GraphQL APIs when you run your Quarkus application. Access the GraphiQL user interface (UI) with the following endpoint after you start your application using Quarkus' dev mode (mvn quarkus:dev):

--/ __ \/ / / / _ | / _ \/ //_/ / / / __/
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \  
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/ 
2020-07-29 22:25:18,324 WARN  [io.sma.graphql] (Quarkus Main Thread) SRGQL010000: Schema is null, or it has no operations. Not bootstrapping SmallRye GraphQL
2020-07-29 22:25:18,552 INFO  [io.quarkus] (Quarkus Main Thread) quarkus-getting-started 1.0.0-SNAPSHOT on JVM (powered by Quarkus x.xx.x.) started in 1.246s. Listening on: http://0.0.0.0:8080
2020-07-29 22:25:18,553 INFO  [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
2020-07-29 22:25:18,553 INFO  [io.quarkus] (Quarkus Main Thread) Installed features: [cdi, resteasy, smallrye-graphql]

Once the Quarkus runtime starts, access GraphiQL's UI locally using http://localhost:8080/graphql-ui/. You will see:


Query the GraphQL API

Try querying GraphiQL using:

query allFilms {
  allFilms {
    title
    director
    releaseDate
    episodeID
 }
}

Click the Play button, and you will see:

Imagine a new client application requires title and episodeID data but doesn't need to invoke the previous API (i.e., over-fetching), which includes unnecessary data (e.g., Director, releaseDate). Try to query GraphiQL again with:

query allFilms {
  allFilms {
    title
    episodeID
 }
}

You will see:

Next steps

GraphQL is only continuing to grow in popularity, and it integrates nicely with Java. If you're looking for more ways to learn, try changing the query data to meet your business requirements over GraphQL APIs. And remember, the complete Java code for this how-to is available in the GitHub repo.


Source: Opensource.com