Type-Safe & Composable GraphQL in Elm

dillonkearns/elm-graphql is a Type-Safe GraphQL Client

Elm is the ideal language for building GraphQL requests. Because it is type-safe and has an expressive type system, you can take full advantage of having a typed API (i.e. your GraphQL Schema). And the Elm Compiler can guarantee that the requests you build up are valid! Using first-class Elm code to build up queries is a great experience, and it has a ton of advantages over dynamically building up strings.

When I built dillonkearns/elm-graphql, I had the following design goals in mind:

  1. Correctness
  2. Type-Safety
  3. Simplicity

In many ways, these values mirror the philosophy of the Elm language itself. So if I could use Elm language constructs rather than a separate DSL as much as possible, I knew I would be well on my way!

Plus, the resulting code would be much more idiomatic and composable. Elm is known for bullet-proof refactoring, so I wanted that same experience refactoring my GraphQL queries and and response data structures. In other words, I wanted to extend the “If It Compiles, It Works” feeling you get writing Elm to the server interactions for Elm apps. I discuss this philosophy in more depth in my Elm Conf talk Types Without Borders.

Let’s dig into to these design goals a little more and see how they play out.

Design Goal 1: Correctness

The Elm compiler must tell you if your query is invalid (argument types are incorrect, required arguments are missing, etc.). In other words, it must be impossible to run the code if the request is not valid according to your GraphQL schema. Let’s see what the Elm Compiler says if we use the wrong name for the required argument login.

Not only does the Elm Compiler tell us something is wrong, it’s able to suggest the correct argument name! No special IDE plugins or linters are required, it’s simply leveraging the Elm Compiler and its famously helpful, human-readable error messages. What’s more, you can refactor as much as you want and the compiler will still be able to help you out! We get all this for free because GraphQL queries with dillonkearns/elm-graphql are first-class Elm code.

Design Goal 2: Type-Safety

Let’s look at a GraphQL Enum type, like the Episode Enum from the oft-cited Star Wars tutorial:

enum Episode {
NEWHOPE
EMPIRE
JEDI
}

In dillonkearns/elm-graphql an Elm Custom Type (also sometimes called Union Types or Algebraic Data Types) is generated for each GraphQL Enum. That means we get all the cool features and type-safety of an Elm Custom Type for free! For example, if we forget to handle a particular episode the Elm Compiler will make us fix it before we can run our code:

You could imagine adding the Prequel or Sequel Trilogy to our Enum on the server side and then being saved by this compiler error. Elm’s Custom Types guarantee that you do an exhaustive check and handle every case.

If you simply check that your Elm code compiles before you deploy any changes to your GraphQL Schema, you can guarantee that you’re handling every possible Enum value in your Elm client code. No need for meaningless default case statements to prevent or log errors!

An added benefit of just using a regular Elm Custom Type for the Episode Enum is that we can use our Episode type anywhere in our Elm codebase. It could be the type of a parameter in any function, or something that we store in our application’s Model, etc.

Design Goal 3: Simplicity

When I started building dillonkearns/elm-graphql, I found it confusing to build queries using other Elm GraphQL clients because there were so many concepts at play. I didn’t realize it at the time, but it’s clear to me now: simplicity is essential for robustness. That is, if you expose a lot of low-level details, you can’t control them and you therefore have no way to guarantee that they are done correctly. Not to mention that it’s much nicer if you can use a high-level API where you don’t have to fiddle around with low-level details and can instead focus on solving problems in your own domain, not the library’s domain.

Consider GraphQL Field Aliases, for example. With a query like this, you would need to use aliases to prevent collisions:

So we have to remember to prefix our requests like this:

smallAvatar: avatarUrl(size: 16)
largeAvatar: avatarUrl(size: 64)

and then remember to look for the data under its smallAvatar or largeAvatar alias in the JSON response.

Consider the exact same query in dillonkearns/elm-graphql:

Notice that there are no aliases. In fact, there is no API for using aliases at all. Instead, aliases are considered a low-level detail and are handled for you under the hood. Your job is just to say what data you want and how to build that raw data up into the desired data structures. If you’re familiar with the way that JSON decoding works in Elm, it’s very similar, except that you don’t describe the underlying data types because they are extracted from your GraphQL Schema.

Composability

Because it’s just plain old Elm code, you can use your favorite Elm refactoring techniques (functions, parameters, modules) to make your GraphQL queries more manageable and expressive. And the feedback/correctness guarantees are just coming from the Elm Compiler, so it’s able to be just as helpful no matter how you factor out your queries. Let’s make an avatar helper function for building up avatars with a size argument.

The Bottom Line

This is just scratching the surface of the experience designing and refactoring API requests in your Elm application using dillonkearns/elm-graphql. When you use existing language constructs for a GrahpQL client, you end up with GraphQL queries as first-class code. So you can enlist all the features the language provides (functions, variables, etc.) to make your queries more maintainable. And if that language is Elm, that means you’re also enlisting the famously helpful Elm Compiler to help you write correct queries and refactor them with ease.

Learn More About dillonkearns/elm-graphql

Check out the learning resources on Github for dillonkearns/elm-graphql, or peruse some examples. Or join us in the #graphql channel in the Elm Slack. There are lots of friendly people in there.

Want to Learn More About Elm?

If you’re new to Elm, you can see my recommended learning resources for beginners. I also specialize in helping teams build highly maintainable frontends with Elm, and I offer free intro talks about Elm to any teams that are looking to experiment. Come say hello in the Elm Slack!