Running GraphQL queries and mutations using a GraphQL client
Prerequisites:
Learn how to use the SmallRye GraphQL client’s typesafe interface to query and mutate data from multiple microservices.
What you’ll learn
GraphQL is an open source data query language. You can use a GraphQL service to obtain data from multiple sources, such as APIs, databases, and other services, by sending a single request to a GraphQL service. GraphQL services require less data fetching than REST services, which results in faster application load times and lower data transfer costs. This guide assumes you have a basic understanding of GraphQL concepts. If you’re new to GraphQL, you might want to start with the Optimizing REST queries for microservices with GraphQL guide first.
You’ll use the SmallRye GraphQL client to create a query
microservice that will make requests to the graphql
microservice. The graphql
microservice retrieves data from multiple system
microservices and is identical to the one created as part of the Optimizing REST queries for microservices with GraphQL guide.
The results of the requests will be displayed at REST endpoints. OpenAPI will be used to help make the requests and display the data. To learn more about OpenAPI, check out the Documenting RESTful APIs guide.
Additional prerequisites
Before you begin, Docker needs to be installed. For installation instructions, refer to the official Docker documentation. You will build and run the application in Docker containers.
Make sure to start your Docker daemon before you proceed.
Getting started
The fastest way to work through this guide is to clone the Git repository and use the projects that are provided inside:
git clone https://github.com/openliberty/guide-graphql-client.git
cd guide-graphql-client
The start
directory contains the starting project that you will build upon.
The finish
directory contains the finished project that you will build.
Before you begin, make sure you have all the necessary prerequisites.
Implementing a GraphQL client
Navigate to the start
directory to begin.
The SmallRye GraphQL client is used to implement the GraphQL client service. The SmallRye GraphQL client supports two types of clients: typesafe and dynamic. A typesafe client is easy to use and provides a high-level approach, while a dynamic client provides a more customizable and low-level approach to handle operations and responses. You will implement a typesafe client microservice.
The typesafe client interface contains a method for each resolver available in the graphql
microservice. The JSON objects returned by the graphql
microservice are converted to Java objects.
Create theGraphQlClient
interface.query/src/main/java/io/openliberty/guides/query/client/GraphQlClient.java
GraphQlClient.java
The GraphQlClient
interface is annotated with the @GraphQlClientApi
annotation. This annotation denotes that this interface is used to create a typesafe GraphQL client.
Inside the interface, a method header is written for each resolver available in the graphql
microservice. The names of the methods match the names of the resolvers in the GraphQL schema. Resolvers that require input variables have the input variables passed in using the @Name
annotation on the method inputs. The return types of the methods should match those of the GraphQL resolvers.
For example, the system()
method maps to the system
resolver. The resolver returns a SystemInfo
object, which is described by the SystemInfo
class. Thus, the system()
method returns the type SystemInfo
.
The name of each resolver is the method name, but it can be overridden with the @Query
or @Mutation
annotations. For example, the name of the method getSystemLoad
is overridden as systemLoad
. The GraphQL request that goes over the wire will use the name overridden by the @Query
and @Mutation
annotation. Similarly, the name of the method inputs can be overridden by the @Name
annotation. For example, input host
is overridden as hostname
in the editNote()
method.
The editNote
mutation
operation has the @Mutation
annotation on it. A mutation
operation allows you to modify data, in this case, it allows you to add and edit a note to the system service. If the @Mutation
annotation were not placed on the method, it would be treated as if it mapped to a query
operation.
Create theQueryResource
class.query/src/main/java/io/openliberty/guides/query/QueryResource.java
QueryResource.java
The QueryResource
class uses the GraphQlClient
interface to make requests to the graphql
microservice and display the results. In a real application, you would make requests to an external GraphQL service, and you might do further manipulation of the data after retrieval.
The TypesafeGraphQLClientBuilder
class creates a client object that implements the GraphQlClient
interface and can interact with the graphql
microservice. The GraphQlClient
client can make requests to the URL specified by the graphql.server
variable in the server.xml
file. The client is used in the querySystem()
, querySystemLoad()
, and editNote()
methods.
Add the SmallRye GraphQL client dependency to the project configuration file.
Replace the Maven project file.
query/pom.xml
pom.xml
The smallrye-graphql-client
dependencies provide the classes that you use to interact with a graphql
microservice.
To run the service, you must correctly configure the Liberty.
Replace the Liberty server.xml configuration file.
query/src/main/liberty/config/server.xml
server.xml
The graphql.server
variable is defined in the server.xml
file. This variable defines where the GraphQL client makes requests to.
Building and running the application
From the start
directory, run the following commands:
mvn -pl models install mvn package
The mvn install
command compiles and packages the object types you created to a .jar
file. This allows them to be used by the system
and graphql
services. The mvn package
command packages the system
, graphql
, and query
services to .war
files.
Dockerfiles are already set up for you. Build your Docker images with the following commands:
docker build -t system:1.0-java11-SNAPSHOT --build-arg JAVA_VERSION=java11 system/. docker build -t system:1.0-java17-SNAPSHOT --build-arg JAVA_VERSION=java17 system/. docker build -t graphql:1.0-SNAPSHOT graphql/. docker build -t query:1.0-SNAPSHOT query/.
Run these Docker images using the provided startContainers
script. The script creates a network for the services to communicate through. It creates the two system
microservices, a graphql
microservice, and a query
microservice that interact with each other.
WINDOWS
MAC
LINUX
.\scripts\startContainers.bat
./scripts/startContainers.sh
The containers might take some time to become available.
Accessing the application
To access the client service, visit the http://localhost:9084/openapi/ui/ URL. This URL displays the available REST endpoints that test the API endpoints that you created.
Try the query operations
From the OpenAPI UI, test the read operation at the GET /query/system/{hostname}
endpoint. This request retrieves the system properties for the hostname
specified. The following example shows what happens when the hostname
is set to system-java11
, but you can try out the operations using the hostname system-java17
as well:
{ "hostname": "system-java11", "java": { "vendor": "IBM Corporation", "version": "11.0.18" }, "osArch": "amd64", "osName": "Linux", "osVersion": "5.15.0-67-generic", "systemMetrics": { "heapSize": 536870912, "nonHeapSize": -1, "processors": 2 }, "username": "default" }
You can retrieve the information about the resource usage of any number of system services at the GET /query/systemLoad/{hostnames}
endpoint. The following example shows what happens when the hostnames
are set to system-java11,system-java17
:
[ { "hostname": "system-java11", "loadData": { "heapUsed": 30090920, "loadAverage": 0.08, "nonHeapUsed": 87825316 } }, { "hostname": "system-java17", "loadData": { "heapUsed": 39842888, "loadAverage": 0.08, "nonHeapUsed": 93098960 } } ]
Try the mutation operation
You can also make requests to add a note to a system service at the POST /query/mutation/system/note
endpoint. To add a note to the system service running on Java 11, specify the following in the request body:
{ "hostname": "system-java11", "text": "I am trying out GraphQL on Open Liberty!" }
You will receive a 200
response code if the request is processed succesfully.
You can see the note you added to the system service at the GET /query/system/{hostname}
endpoint.
Tearing down the environment
When you’re done checking out the application, run the following script to stop the application:
WINDOWS
MAC
LINUX
.\scripts\stopContainers.bat
./scripts/stopContainers.sh
Testing the application
Although you can test your application manually, you should rely on automated tests. In this section, you’ll create integration tests using Testcontainers to verify that the basic operations you implemented function correctly.
First, create a RESTful client interface for the query
microservice.
Create theQueryResourceClient.java
interface.query/src/test/java/it/io/openliberty/guides/query/QueryResourceClient.java
QueryResourceClient.java
This interface declares querySystem()
, querySystemLoad()
, and editNote()
methods for accessing each of the endpoints that are set up to access the query
microservice.
Create the test container class that accesses the query
image that you built in previous section.
Create theLibertyContainer.java
file.query/src/test/java/it/io/openliberty/guides/query/LibertyContainer.java
LibertyContainer.java
The createRestClient()
method creates a REST client instance with the QueryResourceClient
interface. The getBaseURL()
method constructs the URL that can access the query
image.
Now, create your integration test cases.
Create theQueryResourceIT.java
file.query/src/test/java/it/io/openliberty/guides/query/QueryResourceIT.java
QueryResourceIT.java
Define the systemContainer
test container to start up the system-java11
image, the graphqlContainer
test container to start up the graphql
image, and the libertyContainer
test container to start up the query
image. Make sure that the containers use the same network.
The @Testcontainers
annotation finds all fields that are annotated with the @Container
annotation and calls their container lifecycle methods. The static
function declaration on each container indicates that this container will be started only once before any test method is executed and stopped after the last test method is executed.
The testGetSystem()
verifies the /query/system/{hostname}
endpoint with hostname
set to system-java11
.
The testGetSystemLoad()
verifies the /query/systemLoad/{hostnames}
endpoint with hostnames
set to system-java11
.
The testEditNote()
verifies the mutation operation at the /query/mutation/system/note
endpoint.
pom.xml
The required dependencies
are already added to the pom.xml
Maven configuration file for you, including JUnit5, JBoss RESTEasy client, Glassfish JSON, Testcontainers, and Log4J libraries.
To enable running the integration test by the Maven verify
goal, the maven-failsafe-plugin
plugin is also required.
Running the tests
You can run the Maven verify
goal, which compiles the java files, starts the containers, runs the tests, and then stops the containers.
mvn verify
You will see the following output:
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running it.io.openliberty.guides.query.QueryResourceIT
...
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 11.694 s - in it.io.openliberty.guides.query.QueryResourceIT
Results :
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
Great work! You’re done!
You just learnt how to use a GraphQL client to run GraphQL queries and mutations!
Guide Attribution
Running GraphQL queries and mutations using a GraphQL client by Open Liberty is licensed under CC BY-ND 4.0
Prerequisites:
Nice work! Where to next?
What did you think of this guide?
Thank you for your feedback!
What could make this guide better?
Raise an issue to share feedback
Create a pull request to contribute to this guide
Need help?
Ask a question on Stack Overflow