Today I attended a full day workshop on Elm-GraphQL at Elm Europe lead by the package’s author Dillon Kearns.
There were 10 attendees, all with a variety of interests ranging from learning more about the Elm ecosystem generally to adding to their skills for work or home projects. A few including myself were working or had worked in environments with GraphQL but not Elm, so a great opportunity to bring two worlds together.
And what music these two worlds together bring. GraphQL is effectively an enhanced type system that sits between your client front end and your back end data. It allows you to send nested queries without having to set up or distort a REST endpoint for every variation. It’s easy to be specific with what you want from the backend and not ‘overfetch’ or ‘underfetch’ as is easy to do using REST.
Instead you query using object style definitions and set up ‘resolvers’ for every type of data you may want to call. If a type of data has another type of data within it then a resolver is called for the inner data and so on. On the flip side this may mean more individual calls to your database, which may be a problem depending on your application needs and the sophistication of the back end GraphQL server in generating SQL calls.
This whole GraphQL thing came out of Facebook and you can intuitively see how it would have solved a lot of their 'graph' access challenges in pulling together data in different views from different people and types of records. These days GraphQL is used by many companies including GitHub, Shopify and Twitter.
Having a declared schema, introspection, and documentation in whatever GraphQL implementation is server-side gave the opportunity for Dillon to create a code generation tool that gives you the equivalent types and decoders in Elm. That is paired with the Elm package to give a seamless way to retrieve data. So no more doing your own decoders. Dillon also has a related tool for Typescript and Elm.
What this means is you are getting pre-made Elm modules that allow you to build queries that are fully typed and you know will work if they compile (if your GraphQL backend is full-proof of course). All the benefits of the Elm Compiler, edged into the back end.
So, a graphql query just looks like a JS or JSON object but just the keys of the data you want, nested as far as you want. A get query returns the object with the values included.
A graphql mutation query can also return a query but the main point of course is to change particular fields. There is also a subscription query for setting up data change notifications that may typically feedback through web sockets. GraphQL is a transport agnostic spec.
We covered a lot of detail around nullable fields, scalar fields, ENUMs, lists and more. Then we got onto exercises that introduced us to how this all looks in Elm. If you compare side-to-side it’s a little bit verbose and daunting, but once you start playing with it you can see the power.
Elm-GraphQL doesn’t just imitate what you can do in a regular GraphQl client. Dillon described it as operating at a higher abstraction level, because of what Elm itself adds to the equation.
And there's what can be brought back to the compiler from the introspection of the GraphQL server implementation. Being able to get Elm compile errors for invalid queries at compile time is a boon. Having docs in your editor, just being able to write your queries as Elm language not as magic strings.
Dillon also had a focus on the process of writing code that was like a bonus gift in the workshop. For example writing small pieces at a time that compile and work and using dummy values along the way to reduce cognitive load.
Also encouraging us to experiment with pipeline patterns and see how it affected error messages. In Elm-GraphQL you build up your query ‘selection set’ by specifying fields in a SelectionSet.mapN function call or by building a pipeline starting with SelectionSet.succeed. This approach and the tradeoffs will be familiar to some.
I’ve purposefully left code out of this blog post as it would get deep very quickly. I don’t want you to think it’s all a walk in the park. There is a learning curve and it helps if you are confident in the range of Elm language features at the outset.
Even with the smooth code generation in Elm-GraphQL the reality is that two worlds are coming together. You can still find yourself staring at something blankly not sure how it relates to the simplicity of an Graph-QL object. You will import the code that is generated and you need to understand what is in those files. The compiler will help you a lot but sometimes the error messages will flummox.
Nevertheless we could see with the fluidity that Dillon moved around his examples that it is just practice - and in parallel you should get fully up to speed on things like opaque and phantom types as these can help to understand things.
The ability to remove uncertainty is what this is all about, as Dillon has spent much time talking about previously. With the contracts that GraphQL enforces passed over to Elm you are able to have confidence that your queries if they compile will get you the data you want.
It is a pretty good feeling.