Experimental GraphQL

How can we stretch GraphQL to its limits, and beyond?

The best part about GraphQL is that you can learn the entire spec by just hopping on a GraphiQL window and exploring. This makes it tremendously easy to get started. In fact, this is how I learned everything I know about GraphQL, besides one thing . . .

At last fall’s GraphQL Summit, one of the spec-writers, Lee Byron, hinted at the fact that when they introduced mutations to the spec, they later regretted not calling it ‘actions’. This made me pause: “Why aren’t all GraphQL documents just prefixed with ‘action’,” I asked myself, because a query is simply an action. At this point I realized I had yet to figure out the difference between query and mutation, one of the few things you can’t learn from exploring GraphiQL.

It wasn’t until I ran across this paragraph in the graphql-js docs that I figured out the difference: the root fields of a mutation execute in series; queries execute in parallel. And this was the point Lee was making about actions instead of mutations — mutations are not just to signify that data has been mutated, but also that side-effects will occur, and therefore must wait for the previous to finish before starting the next.

For me this seemed like a reasonable consideration, but it’s intent is not fully described by Mutation. When considering the following schema:

if we were to simply add:

not only would we have supported a brand new batched mutation mode for clients to use (which I think is pretty neat), but also fundamentally reversed the intent of what Mutation means — all without writing a single line of code. So, for a query like this:

every field nested under batch will still execute in parallel, even if those fields cause side effects. So this begs the question: “Do we even needQuery and Mutation?

I think not.

A Call to `Action`

Uprooting the two current types and replacing them each with a 3rd, say,Action, cannot happen all at once. However, what I am suggesting is that as a first step we add a third type (with Query and Mutation deprecated), and introduce new syntax or conventions for declaring side-effects. This would not only solve our discoverability problem, but would also add the ability to create “mutations”, or serial actions, at any depth in the document tree.

You have always had the option to create side-effects in your queries, and avoid them entirely in your mutations, so the premise that “Mutations have side-effects” has always been mere convention, and not a very substantial one at that since it only applies to the root of the document. Having a single root operation, action (or even {), is powerful because it uncovers what truly dictates the behavior of the request: the fields, not the operation. The question thats remains is how should we declare how these fields are executed?

Take this request as our motivating example — an action to configure a new virtual game:

Under the current version of GraphQL, this query is possible to create, but you would have to force typical Query root fields under Mutation or vice-versa. With Action, both types work side by side. Now to determine the execution order, there are a few options we can employ. As a first option, graphql could assume that all fields with input arguments are executed in series, and all other fields in parallel. This convention might have its own pitfalls, though. Another option would be to annotate side-effect causing fields in some way, such as adding a @mut directive, taking hints from languages like Rust:

The final option, and maybe the most intuitive one, is to put the impetus of control-flow on the user by creating sequential operations (which requires the often whispered about @exportdirective). Here is an example of what I mean:

By returning the type Action as part of a field’s response, a user can chain further actions to be executed serially with respect to their parent. This pattern would be tremendously powerful for arbitrarily supporting a clients needs, which is the entire point of GraphQL.

I know rebuilding GraphQL from the ground up with this new system is a longshot, so if nothing else, at least now you know why we have mutations in the first place 😄.

Created at carbon.now.sh