Mutations

Learn what a mutation is, how it differs from queries, and how best to design them.

Introduction

So, what’s a mutation? It sounds quite intimidating. Luckily, they are fairly straightforward.

A mutation is just another type of field in a GraphQL schema, the same as a query. In this case, though, a mutation is a standard way to modify server-side data.

In theory, any GraphQL operation could alter server-side data, but the convention is to use the mutation field type to accomplish this.

As the GraphQL specification notes, REST APIs do not prevent you from modifying data via a GET request. But in practice, the convention is to do this with a POST. The same applies in GraphQL. To modify data, use a mutation.

The structure of a mutation

Mutations have a similar structure to queries in that they consist of the field name and the arguments that it accepts.

For example:

mutation createUser(email: String!, password: String!) {
   createUser(email: $email, password: $password) {
      id
      email
      password
}

In theory, you can return a field from a mutation, but a better practice is to return a Payload Type, which is a type dedicated to providing the correct shape for a mutation response.

In the example above, we are creating a user. We might create the following response type:

  type RegistrationMutationResponse {
    message: String!
    user: User
  }

This would allow us to provide a custom success message we could display on the front-end, as well as all the fields available on our newly-registered user.

We can go a step further for a cleaner schema design and create a specific interface for a mutation response like this:

  interface MutationResponse {
    code: String!
    success: Boolean!
    message: String!
  }

This means that every mutation response type that implements the above mutation provides a minimum of these fields. This gives us a very consistent API experience. We always know that we will get a code, success boolean, and message back, along with anything else.

For example, we could then update our RegistrationMutationResponse to become:

  type RegistrationMutationResponse implements MutationResponse {
    code: String!
    success: Boolean!
    message: String!
    user: User
  }

What needs a mutation?

Now that you know how to make a basic mutation, you must figure out how many mutations to make. Do you need to make a single mutation per field, or should you do a mutation per type? Maybe something in between?

When talking about mutations, it is best to think about whether they are fine-grained or coarse-grained.

Generally, this means that a mutation can be very wide and update a lot of things, or a mutation can be very specific and only apply to a single field or entity.

What you decide is generally driven by how you anticipate it being used. A good rule of thumb is to handle creating entities as a broader mutation, but keep updates as individual items.

This approach gives you plenty of flexibility, but it does not create frustrating scenarios when it comes to trying to create entities and needing to handle multiple outcomes at once. Instead, an entity either does or does not exist. For example, you would not want to have a scenario where the blog title was saved, but the body was dropped due to a validation issue. It should be all or nothing.

On the other hand, you would most likely want to update a blog’s title or body on an individual basis without needing to submit the entire blog entity each time. This is where finer-grained mutations shine.

Recap

In this lesson, you learned the basics of mutations and should understand:

  • What a mutation is and how to write one
  • How to structure a mutation in your schema
  • How to write reusable mutation response types to provide flexibility in your schema design
  • When to use a coarse-grained, and fine-grained mutation, and the trade-offs involved in each

Get hands-on with 1200+ tech skills courses.