Over the past few days I’ve been posting about designing mutations in GraphQL. They are often tricky to get right, even more so in a Public API setting. The first post was about what I called Anemic Mutations, and the second one was about designing Static-Friendly Mutations.
Now that we have our behavior-driven and static-friendly mutations, we can move on to trickier things for which I unfortunately don’t think we have a proper solution for yet, but that we can have fun discussing.
State Transitions with Mutations
A lot of APIs offer a way to move a resource / object through a set of transitions. For example, with our e-commerce example, we might want to move a checkout from a “Cart”, to “Checking Out”, to “Payment”, and finally “Order”.
Representing these transitions in a machine readable way is something REST APIs do very well thanks to hypermedia:
A hypermedia-driven site provides information to navigate the site’s REST interfaces dynamically by including hypermedia links with the responses.
Often times, this REST API constraint is solved by having some sort of metadata sent along with with usual entity data. I find this primer on HATEOAS is good to get started if you’ve never heard of this before.
To come back to GraphQL, I truly believe one of it’s biggest strengths are its introspection capacities. It makes a GraphQL Schema very discoverable and machine readable. In fact, we could almost say it is similar to hypermedia in that sense. The GraphQL type system provides information how to navigate the object graph, and expresses all capabilities of the API.
There’s on thing that is missing though. Thanks to the type system, we know the whole set of mutations we can execute, however, after executing a mutation, the client has no idea how to do state transitions from there without reading documentation.
Take our Checkout example again. After adding items to your cart, a client would need to read something to find out it may call
checkout(cart: Cart) for example. After creating the checkout, how does we know we have to go ahead and execute
GraphQL doesn’t have a good solution to this like REST has. In fact, Phil Sturgeon describes the problem very well in his post: https://blog.apisyouwonthate.com/representing-state-in-rest-and-graphql-9194b291d127 I highly recommend checking it out.
As an example of doing this well, we could look at Siren, a hypermedia specification for building APIs. It provides a field
actions with payloads, informing clients of additional actions they make take. For example, maybe we’d have this returned after modifying a checkout:
This is great because we have a machine readable way of executing state transitions. We don’t have to read some kind of tutorial or look trough a lot of docs to know what we’re able to do next. Notice this includes the
href as well as fields to provide when requesting this
href. We even have some auto documentation here by having a title.
If you’re like me, you’re probably wondering if we could have something similar in GraphQL.
What if we took this
actions field and tried to apply it somehow to GraphQL 🤔 Let’s focus on mutations for now.
What if our mutation payloads contained a list of actions?
__Action type here. This type could hold information on a possible state transition:
The cool thing with this type is that we can now use GraphQL’s introspection to get metadata data about these actions! The
description fields help us document actions easily. The
target field replaces the
method , and
fields fields from the Siren example. Because the
target field is of type
__Field, which already exists as part of GraphQL’s introspection type system. Since we reuse that type, we don’t need to describe anything else because it already contains its
args, and even if the field is deprecated.
This means we can explore actions through an introspection query easily and consume it while executing mutations:
I’m really interested to see if we could come up with something like this for GraphQL. I’m hesitant if this should somehow eventually be part of the spec, or “a spec” outside of the GraphQL original one. Maybe even just a “best practice” 🤷♂️
Of course, my examples in this post use the double underscore syntax, which is currently reserved for internal types in GraphQL. If we wanted to implement this outside of the spec, we may have to name things differently.
An other problem might be that clients are not forced to request these fields. Smarter GraphQL clients like Apollo or Relay could maybe automatically inject these in the query strings like they do/did with
What do you think? I’m not 100% sold on this specific solution, but really interested to explore this further 🤔
I hope you enjoyed this experiment, thanks for reading ❤.️ As always, if you’ve enjoyed this post, you could follow me on twitter! In a next post, we’ll explore the problem of “transactions” when running multiple mutation fields in a single operation.
See ya! 👋
Ivan Babak shared a good concern in one of the comments that I wanted to address here:
I agree with your previous articles but not this one. I think machine-readable “hypermedia links” and “actions” in API responses are useless in most cases because you won’t be machine-building a UI from them. The UI will most of the times be built by hand and won’t be aligned one-to-one with either data objects or mutations.
Now that I read this I realize I probably didn’t mention why these “links” or “actions” are useful for us when building APIs. It’s true that we won’t often be “machine-building” our UIs. Like Ivan said, UIs are often built by hand. This doesn’t mean using hypermedia is only useful to our friendly computers.
- They make our API more explorable. Even if you’re building a UI by hand, chances are you will be trying out a mutation, or a REST endpoint. Instead of looking through the docs or tutorials, you’ll be guided through the next state transition, and get a mental model of the API quicker.
- Documentation in Schema. Just like you could say descriptions are useless on the introspection schema, since they require a human to read them, having well documented transitions / actions allows someone to understand the flow of the API quicker.
- They make Schema Evolution / API evolution easier if clients follow these links. The mutation or endpoint to call for a transition can change and clients simply follow this link.
- For REST APIs especially, they remove the need for dynamically built hrefs, IDs and variables. We could apply the same to GraphQL as well depending on scenarios.
- Bonus random idea: They would probably make schema stiching and the likes really fun to use also if you included URIs.