GraphQL Object Notation

After making some refactorings in sangria recently, I realized that GraphQL actually has a very nice syntax for an input objects. Just look at this example:

{
id: "1234345"
version: 2 # changed 2 times

deliveries: [
{id: 11, received: false, note: null, state: OPEN}

# delivered a bit too late
{id: 45, received: true, note: "short delay", state: CLOSED}
]
}

Here I use a subset of GraphQL query language that is used to define argument values and described in a great detail in the “Input Values” part of the spec. It looks similar to JSON, but has number of improvements in comparison to it:

  • comma is optional (in fact it’s treated as a whitespace)
  • comments!
  • enum values
  • object properties are written without quotes

A nice middle-ground between YAML and JSON. Sometimes I use YAML (mostly because I want to write the comments), but its indention-based object and list nesting confuses me a great deal most of the time.

After this realization I started to wonder whether it would be possible to use this “GraphQL Object Notation” independently from GraphQL query. It turns out that it’s not only possible, but also works pretty well.

After some hacking on the the library and exposing related functionality I ended up with something like this:

A formatted output will look like this:

{
id: "1234345"
version: 2
deliveries: [{
id: 11
received: false
note: null
state: OPEN
}, {
id: 45
received: true
note: "short delay"
state: CLOSED
}]
}

So as you can see, with sangria v0.5.0 you now can parse and format GraphQL input objects independently from the queries. As demonstrated in the example, you can use graphqlInput macro, which will produce a nice compilation errors if you mess up the syntax:

Test.scala:17: Syntax error while parsing GraphQL query. Invalid input '"', expected NameChar, ObjectFieldConst or ws (line 7, column 41):
{id: 11, received: false, note: oops"foo", state: OPEN}
^
graphqlInput"""
^
one error found
(test:compileIncremental) Compilation failed

Of course you can also parse input objects dynamically with QueryParser.parseInput:

QueryParser.parseInput("{foo: true, bar: [1, 2, 3]}")

So if nothing else, at least you can use sangria and GraphQL as a nice JSON alternative :)

End-To-End GraphQL

GraphQL is agnostic in terms of data format of execution results and variables. Normally people use JSON for this, but it can be any format that support scalar values (like string or boolean), dynamic map-like and list-like data structures.

Just as a fun experiment I decided to add support of “GraphQL Object Notation” in sangria. It already has an abstraction for variable deserialization in form of InputUnmarshaller and for execution result serialization in form of ResultMarshaller. After implementing both of these traits for sangria.ast.Value I was able to use it as a data format for query execution. Here is an example of how you can use it yourself (you just need to import sangria.marshalling.queryAst._ and executor automatically understand how to handle sangria.ast.Value):

Here is the output:

{
data: {
hero: {
id: "1000"
name: "Luke Skywalker"
appearsIn: ["NEWHOPE", "EMPIRE", "JEDI"]
}
human: {
name: "Leia Organa"
friends: [{
id: "1000"
name: "Luke Skywalker"
}, {
id: "1002"
name: "Han Solo"
}, {
id: "2000"
name: "C-3PO"
}, {
id: "2001"
name: "R2-D2"
}]
}
}
}

So it’s a complete end-to-end GraphQL! GraphQL comes as an input to an execution in a form of query and variables and GraphQL is produced as a result of an execution. JSON is not involved in any way at any given point.

I’m not sure how practical and useful this is, but it’s definitely interesting and fun experiment which turned out to be pretty successful :) All described functionality is already released and available in sangria v0.5.0, so please feel free to check it out and play with it yourself.

Show your support

Clapping shows how much you appreciated Oleg Ilyenko’s story.