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.
1 Comment