Skip to content

OpenLiberty/guide-graphql-client

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Running GraphQL queries and mutations using a GraphQL client

Note
This repository contains the guide documentation source. To view the guide in published form, view it on the Open Liberty website.

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.

GraphQL client application architecture where multiple system microservices are integrated behind the graphql service

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.

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 the GraphQlClient interface.
query/src/main/java/io/openliberty/guides/query/client/GraphQlClient.java

GraphQlClient.java

link:finish/query/src/main/java/io/openliberty/guides/query/client/GraphQlClient.java[role=include]

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 the QueryResource class.
query/src/main/java/io/openliberty/guides/query/QueryResource.java

QueryResource.java

link:finish/query/src/main/java/io/openliberty/guides/query/QueryResource.java[role=include]

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

link:finish/query/pom.xml[role=include]

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

link:finish/query/src/main/liberty/config/server.xml[role=include]

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.

.\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:

.\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 the QueryResourceClient.java interface.
query/src/test/java/it/io/openliberty/guides/query/QueryResourceClient.java

QueryResourceClient.java

link:finish/query/src/test/java/it/io/openliberty/guides/query/QueryResourceClient.java[role=include]

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 the LibertyContainer.java file.
query/src/test/java/it/io/openliberty/guides/query/LibertyContainer.java

LibertyContainer.java

link:finish/query/src/test/java/it/io/openliberty/guides/query/LibertyContainer.java[role=include]

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 the QueryResourceIT.java file.
query/src/test/java/it/io/openliberty/guides/query/QueryResourceIT.java

QueryResourceIT.java

link:finish/query/src/test/java/it/io/openliberty/guides/query/QueryResourceIT.java[role=include]

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

link:finish/query/pom.xml[role=include]

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!