GraphQL Mutation Design: Anemic Mutations

by Marc-Andre Giroux

Mutations are one of the trickiest part of a GraphQL schema to design. We spend a lot of time talking about GraphQL queries and how easy they are to use. However, mutations get far less love.

I spent the last few days thinking about how I think about mutation design, and how it relates RPC, REST, & domain driven design. I’ll be posting a series of posts on a few subjects related to GraphQL Mutation Design.

In this post, we’ll focus on what I’ve been calling Anemic Mutations.

Anemic Mutations ™️

There’s this thing called Anemic Domain Models in the domain driven design world. Explained quickly, the AnemicDomainModel is a pattern in which our domain model contains only data, without any of the behaviors associated with it.

I think we can make a pretty interesting link between this “Anti-Pattern” and designing a good mutation system in our GraphQL APIs. Here’s an example of what I would describe as an Anemic Mutation. Imagine a Checkout object, part of some e-commerce GraphQL schema:

The first thing we can look at is how all the input fields on UpdateCheckoutInput are optional. Since they have opted for a simple CRUD mutation, and since they might want to allow partial updates during the checkout process, it makes sense at first to make everything optional. There’s a few things about this design that I really don’t like.

First, since we decided to use a “coarse grained” mutation that allows us to make any changes to this Checkout object, we had to make everything nullable (optional). One of GraphQL’s great strengths is its type system. By removing any nullability constraints on the input fields, we’ve pretty much deferred all this validation to the runtime, instead of using the schema to guide the API user towards proper usage.

The second point is what makes this mutation an AnemicMutation. We’ve designed the input type in a very data-centric way, instead of focusing on behaviors. Imagine for example that our API user wants to use the API to create a “Add To Cart” button:

Lets look at how we could design this mutation so that it explicitly tells us how to add an item to a checkout:

We’ve fixed most of the issues we talked about earlier:

An interesting side-effect of designing mutations this way is that developing these mutations becomes way more straight forward on the backend. Since the mutation input is way more predictable, we can remove a lot of non-needed validations in the resolver. An other really cool thing is that emitting events also becomes easier. Imagine trying to emit a ItemAdded event every time some user adds an item to their checkout. In a big fat mutation with optional input fields, we need to check for every scenario with conditionals, and emit events depending on these conditions, it gets messy.

In a way, I find this ties in a lot with a few points Caleb Meredith made in his post (https://dev-blog.apollodata.com/designing-graphql-mutations-e09de826ed97) a while ago, which I really enjoyed.

Over the coming days and weeks, I’ll be publishing a few other posts on GraphQL mutation design. I’ll be writing on managing state with mutations, designing for static queries, and good argument design.

Thank for reading ❤️ If you’ve enjoyed this post, you could follow me on twitter! You would probably also enjoy The Little Book of GraphQL Schema Design that I’m working hard on finishing. It would mean a lot if you would subscribe for updates!