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 theGraphQlClientinterface.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 theQueryResourceclass.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:
WINDOWS
MAC
LINUX
mvnw.cmd -pl models install
mvnw.cmd package
./mvnw -pl models install
./mvnw package
./mvnw -pl models install
./mvnw package
The Maven install goal 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 Maven package goal 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
./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
./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.javainterface.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.javafile.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.javafile.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.
WINDOWS
MAC
LINUX
mvnw.cmd verify
./mvnw verify
./mvnw 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:
Great work! You're done!
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