[This post is woefully out of date, read the Update]
In my prior post on GraphQL server in Java I noted the complexity of defining the schema and committed to look at the tools offered to ease the process. Here we are. I’ve migrated the schema definition over to using an annotation package and as I’m generally pleased with the results as it really did simplify the schema definition.
Class Definitions
Defining the classes was trivial. Simply adding a few annotations to the entity getter methods:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Once those are in place, anywhere you reference that type in your queries or mutations will “do the right thing.”
Queries/Mutations
This was a bit less polished, but worked well enough too. They offered a couple of approaches and in the end I implemented the following pattern:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
There a few things going on here to note. First the annotation tool scans the .class definition, so either you use static methods, or if you use instance methods it’s going to call the no argument constructor to create and instance. Since data fetchers need data, I was a little confused about what to do with static methods, or a no argument instance – how do you get your data access objects in there? Obviously you could make you DAO objects into singletons and get at them that way but that seemed ugly. What I found, by digging into the code was that if your GraphQL dispatcher passed in a context object that was accessible via the DataFetchingEnvironment.getSource() method. So I went with static methods and as you see in line 6 above I access the DAO via getSource().
Using the Annotated Classes
Once you’ve annotated your entities and created an annotated query and mutation class here is how to create your schema:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
I felt the annotations definitely made the code simpler and cleaner. About my only complaint was that the documentation and examples were so terse that I ended up tracing through the code to figure out how some of it came together.
[This post is woefully out of date, Please read Update here!]
Github is a service I respect and depend on so its adoption of GraphQL for an update to their API pushed me to look into GraphQL.
The Approach
People suggest a good place to start with GraphQL is to migrate an existing RESTful service, so I decided to try it out on one of my existing Java RESTful services. That service has a Java backend with a simple jQuery based JavaScript UI. So I started looking for a Java based GraphQL server and a simple JavaScript GraphQL client.
For the client side I reviewed several offerings but they were all rather complex for my needs, so with about 40 lines of JavaScript I cooked up my own client.
I also decided to follow the generally accepted wisdom that you only tackle the data management parts of your API with GraphQL, and leave things like authentication as they were.
The Server
The graphql-java package came with some good examples that let me start trying it out right away. But the service I was migrating was based on tools that didn’t exactly line up with their examples so it wasn’t plug and play. My service was written with Spark: “A micro framework for creating web applications in Java 8 with minimal effort.” And as it turned out connecting graphql-java to Spark was straight forward. Associate something like the following with the graphql HTTP POST path:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
And your service can now dispatch GraphQL requests in JSON of the form:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
With that dispatcher in place you can start the process of working on your GraphQL schema implementation, where the magic takes place.
The GraphQL Schema
The schema, at least in graphql-java, is where a lot of the work starts and ends. In a RESTful service, you tend to map a single HTTP request path and operation to a specific Java method. With graphql-java the schema is a holistic description of types, requests, and how to fulfill them. It almost has the feel of a Guice module, or a Spring XML configuration. There are tools designed to simplify schema creation, but since I had set out to learn GraphQL I decided to feel the pain and create the schema by hand. Every type exposed by the API had to be described. Every request had to be described. The associations between the requests and the code fulfilling them had to be described. Again the graphql-java examples and tests proved a good resource but nothing there was a drop in solution.
Describing an Entity
The service I ported was a snippets app. One of of its domain classes is a way to categorize a snippet:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
To add this class the the GraphQL schema you have to fully describe it:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Once you’ve described the classes, you need to describe how you’ll retrieve instances, here’s an example of retrieving by key, and retrieving them all:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
To create, update or delete objects you’ll need to define the “mutations”, here’s my category create:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From these samples you can tell that the schema is, as the name implies, a detailed description of the classes and operations on them. The format is verbose, but I found I pretty quickly picked up the syntax and semantics and developing the complete schema wasn’t too bad a chore. Also, don’t forget there we tools claiming to ease the process that might be useful.
The Client
I developed the server side to completion using various testing to drive that. Once I had a server that supported my old RESTful operations via GraphQL I approached the client side. My client was nothing more then jQuery performing HTTP get/post/deletes, and it wasn’t particularly tidy. I looked into existing JavaScript clients, but they seemed to all be npm based or integrated into a framework. So I just wrote the following:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Both the server and the client benefitted from GraphQL’s more holistic approach. Where GraphQL had a single schema, dispatcher and client pattern, RESTful had used GET/POST/DELETE methods each somewhat distinct.
The performance suffered a bit. I’ve done a lot or RESTful services, and this was my first foray into GraphQL so I’m not surprised that it wasn’t quite as quick end to end. I’m suspicious of some of the magic (likely reflection based) that graphql-java uses to execute the schema queries. That said I’m betting I can improve the performance by doing some things better then my first attempt.
But overall I liked the GraphQL experience and will probably advocate it over RESTful going forward.
As always the complete code for my work is in github.