GraphQL: Java Server, JavaScript Client

[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 server side I settled on graphql-java.
  • 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:

private Map<String, Object> graphql(Request request,
Response response) {
Map<String, Object> payload;
payload = getMapper().readValue(request.body(), Map.class);
Map<String,Object> variables =
(Map<String, Object>) payload.get("variables");
ExecutionResult executionResult =
graphql.execute(payload.get("query").toString(), null, null, variables);
Map<String, Object> result = new LinkedHashMap<>();
if (executionResult.getErrors().size() > 0) {
result.put("errors", executionResult.getErrors());
}
result.put("data", executionResult.getData());
response.type("application/json");
return result;
}

And your service can now dispatch GraphQL requests in JSON of the form:

{
"query": "{ categories{ key name }}",
"variables": {}
}

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:

public class Category {
private String name;
private String key;
/* Constructors, Gettters, Setters */
}

To add this class the the GraphQL schema you have to fully describe it:

GraphQLObjectType category = newObject()
.name("category")
.field(newFieldDefinition()
.name("key")
.type(GraphQLString)
.build())
.field(newFieldDefinition()
.name("name")
.type(GraphQLString)
.build())
.build();

Queries and Mutations

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:

return newObject()
.name("query")
.field(newFieldDefinition()
.name("category")
.type(category)
.argument(newArgument()
.name("key")
.type(new GraphQLNonNull(GraphQLString))
.build())
.dataFetcher(environment -> categoryDao.findOne(environment.getArgument("key")).orElse(null))
.build())
.field(newFieldDefinition()
.name("categories")
.type(new GraphQLList(category))
.dataFetcher(environment -> categoryDao.findAll().collect(Collectors.toList()))
.build())
.build();

To create, update or delete objects you’ll need to define the “mutations”, here’s my category create:

return newObject()
.name("mutation")
.field(newFieldDefinition()
.name("category")
.type(new GraphQLTypeReference("category"))
.argument(newArgument()
.name("key")
.type(new GraphQLNonNull(GraphQLString))
.build())
.argument(newArgument()
.name("name")
.type(new GraphQLNonNull(GraphQLString))
.build())
.dataFetcher(env -> {
Category category = new Category(env.getArgument("key"),
env.getArgument("name"));
categoryDao.save(category);
return category;
})
.build())

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:

var Graphql = function (url, query) {
"use strict";
var _this = this;
this.url = url;
this.query = query;
this.variables = {};
this.toString = function () {
return JSON.stringify(_this);
};
this.execute = function (consumer) {
console.log("GraphQL Request: " + _this);
$.ajax({
url: _this.url,
async: true,
method: "POST",
contentType: "application/json",
data: _this.toString(),
dataType: "json",
success: function (response) {
if (response.errors != undefined) {
console.log("GraphQL Errors: " + JSON.stringify(response.errors));
}
consumer(response);
},
error: function (jqXHR, textStatus, errorThrown) {
console.log("Request failed: " + textStatus);
console.log(errorThrown);
}
});
};
}
view raw BP3_graphql.js hosted with ❤ by GitHub

With that my JavaScript could do things like:

var categoryGQL = new Graphql("v1/graphql", "{ categories { key name }}");
categoryGQL.execute((response) => loadCategories(response.data.categories));

My Conclusions from the Experience

So with a couple days of learning and coding I got my snippets service moved from RESTful to GraphQL API.  What are my initial observations?

  • The work is in the schema.  I’ll be looking at the tools to help there in my next refactoring.
  • 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.

14 thoughts on “GraphQL: Java Server, JavaScript Client

  1. Pingback: GraphQL: Java Schema Annotations | My name is Ozymandias
  2. Pingback: Java GraphQL Server in Ratpack | My name is Ozymandias
  3. Great post thank you. I have just did it on my local and it works perfectly. I am just wondering how can we do the field validation. Lets say the model User has the Email field and we want to validate this field (whether the email contains character:@) during the user mutation, then how can we do it?

    Best,
    Karoly

  4. can you please explain how to add data into two different tables with have one to many mapping.
    for example i have two tables like author and post . author table has one to many mapping t o the post table . i want to add a new author with a post .

    can u please help me..

  5. Hi,

    Thanks for the Post!.
    Could you please clarify the following, Say there is a rest service which returns an object with 10 fields, now say I want to fetch
    only two fields. If i wrap graphQL around this service, then will it still query all the 10 fields and return only requested fields ?.

    or will it fetcg only two fields from the query(which was not modified by dev)?. how does this work, what problem is this solving ?

    • I’m not sure it “solves” things… but I find it simpler.

      Take for example, you have a rather rich entity object that’s part of an API, say User, with maybe 20 attributes. Different parts of the UI need different bits and pieces of it: just the name, the name and address, the name and department, etc. There a various approaches to dealing with this. Offer a single API returning the full objects and let the UI deal with filtering. Offer different services with “full” and “light” versions. You can have the service accept a field filter. And depending how you serialize and deserialize adding/changing fields may impact a fair bit of your code.

      GraphQL just formalizes the filtering approach, and does it in a way that’s orderly so the impact of object attribute changes can be localized to just the code that cares. It’s not “solved”… API contracts are still contracts and that’s not magically solved, but I’ve found it’s a much better framework for flexibly dealing with data them then RESTful APIs which usually seem very brittle and resistant to change.

  6. Pingback: GraphQL Java 6.o | My name is Ozymandias

Leave a reply to nwillc Cancel reply