Categories
Jakarta EE

Developing a GraphQL client on MicroProfile with Quarkus

Following the release of MicroProfile GraphQL 1.0, Phillip Krüger has recently published an article introducing what is GraphQL and how to develop a server.

Here, we propose to focus on the client-side and illustrate how to interact with a GraphQL endpoint using Java in a JakartaEE and MicroProfile context.

This article is backed by a GitHub project. Feel free to use it.

Choosing a GraphQL Java client

MicroProfile GraphQL 1.0 does not include a client API. The focus of the first release has been deliberately put on the server-side: how to properly expose data and operations as easily as possible following a code-first approach.

A client API is scheduled for 1.1. The client API will be delivered in two flavors: a low-level one and a high-level one. To make an analogy with existing REST specifications: the low-level API will be similar to Jakarta RESTful Web Services (previously JAX-RS) and the high-level one close to MicroProfile Rest Client.

For this project, we first need to chose an existing GraphQL Java client. I’ve identified 3 of them:

  • A Simple GraphQL client: where GraphQL operations (queries or mutations) are managed as Strings and JSON responses are deserialized by a lambda function. This is very low-level but may be sufficient for basic requirements. To make it more JakartaEE/MicroProfile friendly, I’ve adapted it to use JSON-B and JSON-P (see my GitHub project basic-graphql-client)
  • Manifold: is much more high-level and proposes a disruptive approach using GraphQL syntax as “single source of truth”. You do not need to implement specific Java classes dedicated to the mapping, you just code GraphQL operations and the bytecode is automagically generated in your IDE (a specific IntelliJ plugin is needed). For more information, you can visit my Manifold evaluation project on GitHub
  • Nodes: is a Java GraphQL client library developed by American Express. It offers a simple yet powerful API where we develop classes with specific annotations to ensure the mapping with GraphQL operations and results. For more information, please visit my Nodes evaluation project on GitHub.

For this article, I propose to use Nodes which offers a good compromise between functionalities, ease of use and integration.

Exploring the person-example

Phillip’s article is backed by a graphql-example project published on GitHub.

To run it:

  1. Fork or download the GitHub repository
  2. Switch to Java 8 (the example currently doesn’t work with newer version)
  3. cd person-example
  4. mvn thorntail:run
  5. Interact with the GraphQL endpoint using GraphiQL from http://localhost:8080/graphiql/

GraphiQL is a JavaScript-based web GUI that enables to discover any GraphQL schema and run operations against it. We can browse the schema with the Documentation Explorer pane on the left:

Here, we have two root queries named Query and Mutation. By following the links, we can navigate and discover all exposed operations and data.

GraphQL vs REST

In a few words, what make GraphQL really different from REST:

  • It is natively auto-discoverable from the schema. No additional metadata similar to OpenAPI is required
  • It is strongly typed: the schema defines the data, their types and how to interact with them through operations. The schema defines as an explicit contract between the server (aka endpoint) and its clients
  • It is extremely flexible for the client: for each operation, the client defines the selection fields in return. In a single application, you can have as many representations of the same entity as you want
  • It enables partial results. This is not an “all or nothing” scenario. We can get both errors and partial data in a response.

That doesn’t mean that GraphQL is “better” than REST! At the risk of over-simplification, I would say that REST is a good choice for process-oriented APIs and GraphQL for data-oriented APIs. In saying that, I don’t want to start a polemic! They are more friends than enemies and both can be used in applications.

Using Nodes on Quarkus

We’re going to leverage Phillip’s example and code a Nodes client. To make it more fun and realistic, let’s do it using MicroProfile on Quarkus!

The project is made of 2 services (Person and Profile) each made of 3 layers following the Entity Control Boundary model:

  1. A boundary layer exposing a fully-documented REST API
  2. An entity layer holding the data used by GraphQL operations and results
  3. A control layer making use of Nodes specific API, running GraphQL operations.

To play around with it, all you have to do is let yourself be guided by the Swagger GUI (go to http://localhost:9090/swagger-ui). To get familiar with the GraphQL syntax, operations and responses are logged in INFO mode on the console. We can copy-paste and run them with GraphiQL.

Of course, in real life this kind of direct mapping from REST to GraphQL, with no intermediate business logic, wouldn’t make sense. This is just for the purpose of learning.

Quarkus configuration

A few words about Quarkus configuration:

  • To avoid port conflict with the person-example backing service (default port 8080), Quarkus has been configured to run on port 9090
  • To enable the Swagger GUI in production mode, quarkus.swagger-ui.always-include has been set to true
  • To enable the application to run in native mode using GraalVM, classes that are subject to introspection must be annotated with @RegisterForReflection. To make it simple, all classes in entity packages have been annotated with it
  • Two Nodes classes must also be declared explicity for introspection. To avoid changing their code, as explained in the project README, it is necessary to declare them in a config file.

Coding with Nodes

As expected, Nodes has been easy to use in a MicroProfile context, in particular with Quarkus (both in JVM and native mode). It just depends on Jackson which makes it easy to integrate.

Nodes programming model is strongly-typed and based on annotated Java classes mapping to GraphQL operations and results. For instance, to retrieve a person by her id and get back her surname, names, birthDate and list of addresses:

// Other annotations to mentioned here
@GraphQLProperty(name = "person", arguments = {
    @GraphQLArgument(name = "personId")})
public class FindPersonByIdRequest {
    int id;
    String surname;
    String[] names;
    LocalDate birthDate;
    List<PersonByIdAddress> addresses;
}

It took me a little while to get comfortable with it, but once you’ve grasped the concepts, you’re productive.

The most complicated part from a programming perspective has been to cope with GraphQL native flexibility. For instance in my code, there are several representations of the same Person entity. That means several Java classes representing the same business entity: AddPersonRequest, DeletePersonRequest, PersonByIdRequest … Naming, organizing and ensuring consistency between them on a real project might be a challenge. A tradeoff must be found between GraphQL flexibility and Java strong-typing to limit the risk of class proliferation and inconsistency.

Conclusion

In this article, we have showcased how concretely we can develop MicroProfile GraphQL applications:

  • On the server-side: we have reused the example Phillip Kruger example which demonstrates MicroProfile GraphQL 1.0
  • On the client-side: we’ve used Nodes, an easy-to-use Java client API developed.

What are the next steps?

First off, more implementations of MicroProfile GraphQL are expected. For the moment Phillip’s example runs on Thorntail. We expect OpenLiberty, Quarkus, WildFly, TomEE, Payara, KumuluzEE and others to propose their implementation. In parallel, we’re working on a GraphQL client API with release 1.1.

So stay tuned and be ready to boost your enterprise APIs with GraphQL. Hope this help!

By Jean-François James

Senior Software Architect, Worldline Fellow Expert, Open Source contributor, blogger and speaker, father of 3, martial artist.

One reply on “Developing a GraphQL client on MicroProfile with Quarkus”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s