Jan 4, 2022

What I learned from reading the GraphQL Spec

Blog post's hero image

How did you learn GraphQL? I did it by hands-on building stuff with it, with a lot of help from online tutorials, search engines, and time.

If you started your journey with GraphQL by reading the full specification, then kudos! In my case, it took several years of working with GraphQL - from side-projects to production APIs - and a quiet week of vacation until I had the muse to take a look into that long document.

Turns out that this spec is very well written! There aren't many prerequisites to get started and most sections are accompanied by descriptive examples. In fact, after smashing through the whole document in two days, I walked away with a deeper understanding of GraphQL, and I even learned some new tips for a language I've been using for years.

Block Strings

Being able to document your API inline with your schema definition is an awesome feature of GraphQL. Fun fact: It doesn't actually show up in the "Introduction to GraphQL" section, so here's a quick refresher:

type User {
"""
You can document here what this field contains
"""
name: String
}

Initially it seemed to me that the triple quotes are a special syntax, but it's actually just regular string tokens. GraphQL defines both "block strings" (the ones with triple quotes) and regular "strings", but they are both parsed as the same token. You might have known that (I felt quite stupid after reading that), but did you know:

The GraphQL spec states that the value of a block string has to be trimmed and dedented when parsing the document.

That means even though the block string in the example above contains two newlines and a tab, the resulting value will simply be You can document here what this field contains. That's pretty neat, and it comes in very handy when passing multi-line strings as arguments.

Descriptions

Since we're talking about descriptions, did you know that:

GraphQL allows and endorses using Markdown in descriptions.

That means you're not limited to plain text when it comes to documenting your schema. You can include links to other resources, show code snippets with syntax highlighting, or even embedd images and videos! Both GraphiQL and GraphQL Playground parse descriptions as Markdown and include the resulting HTML in the docs tab.

Coercion

The spec talks a lot about how a GraphQL server should coerce values to a certain type. (Searching for coerc yields 148 results in the latest release!) There are two kinds of coercion that the spec defines for all kinds of types and built-in scalar types:

  • "Result coercion": How to coerce values returned by a resolver to the type given by the schema.

  • "Input coercion": How to coerce a value passed as argument or variable to the type given in the schema or operation.

The spec goes the most into detail about the latter one, and there's one thing that I never would have guessed is valid GraphQL:

When passing a singular non-null type as input to an argument that expects a list type, the result of GraphQL input coercion is a list of length one where the single item is the passed value.

That's a mouthful, so let's do a concrete example using this schema:

type Query {
"""
Calculates the sum of all passed summands. Examples:
- Querying `sum(summands: [])` returns `0`
- Querying `sum(summands: [1])` returns `1`
- Querying `sum(summands: [1, 2, 3])` returns `6`
"""
sum(summands: [Int!]!): Int!
}

Given that, all following queries will return the same result:

query PassAList {
sum(summands: [1])
}
# Passing a single value works just as fine
query PassASingleValue {
sum(summands: 1)
}
# It even works when passing it as variable,
# i.e. when sending the following query with
# `{ variables: { summands: 1 } }`
query PassAVariable($summands: [Int!]!) {
sum(summands: $summands)
}

Execution

When handling a GraphQL request, this is the phase where the server will actually go through all the selection sets and calls your resolvers. After writing GraphQL servers in Node.js for some time, the function signature for a resolver has probably been permanently imprinted in your memory.

function myResolver(root, arguments, context, info) { // Do stuff...}

After reading the spec section about execution, I came to a realization:

Neither the context nor the info objects are mentioned anywhere in the spec.

This is probably the least practical thing I learned, but it was interesting to see that the spec intentionally leaves out such "implementation details". You could start implementing your own GraphQL server with a different abstraction for sharing objects between resolvers, and it would still align perfectly with the spec.

Serialization

The GraphQL spec also shortly talks about how the execution result should be serialized. Fact is:

GraphQL does not require a specific serialization format.

The most common format is JSON, but it's actually not optimally suited for GraphQL. Objects in JSON are unordered, but selection sets in GraphQL are ordered. If a serialization format supports ordered maps, the order of items should mirror the order of fields in the selection set.

JSON gets its own subsection, where the spec at least asks for this:

JSON (or other textual serialization formats) should preserve the order of requested fields textually.

That means that given the query { name, email }, the JSON response should be {"name":"Thomas","email":"thomas@graphcdn.io"}, but not {"email":"thomas@graphcdn.io","name":"Thomas"}.

GraphQL over HTTP

After I finished reading the spec, the last thing I noticed is that it does talk about the most common use-case of GraphQL:

GraphQL is not limited to serving an API over HTTP.

This leaves the door open to use GraphQL as interface language for completely different kinds of systems. It also means that there are no hard requirements yet on how GraphQL APIs should serve content over HTTP (just think of status codes). There is however active development on writing out a separate spec on this!

Now go read it!

If you learned a thing or two by reading this post I highly recommend reading through the spec for yourself! Even if you already knew all of this, it's worth checking in on the spec once in a while. The latest draft is developed very actively, and just back in October there even was a new official release. I promise it's gonna be worth it!