DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Cover image for Best practices for GraphQL mutations
Fauna for Fauna, Inc.

Posted on • Originally published at fauna.com

Best practices for GraphQL mutations

When you mention GraphQL queries, everyone only thinks about querying data. However, applications also need to support full CRUD behaviors, enabling actions like updating and deleting data. This means that there is more than GraphQL APIs need to do.

In a GraphQL API, a user can perform three types of operations: queries, subscriptions, and mutations. GraphQL queries are used to fetch data from a server. Subscriptions are long-lived requests that fetch data in response to certain trigger events. Finally, a mutation is used to write or modify server-side data.

In this guide, we will delve deeper into GraphQL mutations, how they are structured and defined, and some of the best practices involved in using GraphQL mutations.

What are GraphQL mutations?

Before getting into mutations, let’s introduce GraphQL.

GraphQL is a query language for interacting with APIs. In recent years, it has grown in popularity as a more flexible and performant alternative to traditional REST APIs for certain use cases.

In GraphQL, mutations are used to create or modify server-side data. This means that they are used to β€œmutate,” or change information on the server. This is equivalent to the state-changing REST methods(DELETE, PUT, POST, and PATCH).

How to use mutations in GraphQL

Understanding the structure of a GraphQL query and how these GraphQL operations work will be helpful when we discuss some of the best practices for designing GraphQL mutations in the following section.

The graphic below outlines the structure of the GraphQL operation. First the operation type is specified. Next, every operation is assigned its own unique name. In this example HeroNameAndFriends is the operation name. In some cases, operations may also require variables to be passed in as arguments. These variables are specified in the variable definition. In this case, the variable is $episode, which is of the type Episode.

Variable definitions are expressed in the form ($variable_name: VariableType). You can include multiple variable definitions by adding them as comma-separated values.

The operation types can be either a query, subscription, or mutation. The following example illustrates a query operation that fetches data in the same structure specified in the operation.

query HeroNameAndFriends ($episode: Episode) {
hero(episode: $episode){
name
}
} 
Enter fullscreen mode Exit fullscreen mode

The following example illustrates a mutation operation. We signal the mutation method that we want to invoke by naming it exactly as it occurs within our server-side GraphQL API. Here, the addBook mutation creates a mutation that adds a book to an API that stores the book in a library. Information about the title and author are provided to the mutation through the variable definitions ($title: String!, $author: author).

mutation AddNewBook ($title: String!, $author: author) { 
  addBook(title: $title, author: $author) {
    title
    author
  }
} 
Enter fullscreen mode Exit fullscreen mode

Like queries, mutations also return data. Mutations, however, will only return new data that is created. After the mutation is successfully executed, the created book is returned by the API in the format:

{
    title
    author
} 
Enter fullscreen mode Exit fullscreen mode

For this mutation, named addBook, to be successfully registered in our GraphQL API, it would also have to include a GraphQL API type definition in the root mutation object as shown below:

type Mutation {
  addBook (title: String!, author: author): AddBookResult!
} 
Enter fullscreen mode Exit fullscreen mode

The structure includes the name of the mutation, the variable definitions and the result of the mutation. Here the variables are defined as (title: String!, author: author). The trailing ! is used to specify required variables for the mutation. For this mutation to be successfully executed, you must pass in a String value for the title. However the author field is optional. Similarly, the AddBookResult! Return type specifies that this mutation must always have a return value.

Best practices for GraphQL mutations

In general, mutations represent actions on an object. Here are some key best practices for GraphQL mutations :

  1. Use short, but descriptive names to describe the mutation actions. The easiest and best way to represent mutations is to start with an action verb first followed by the name of the object that the action is being applied to. For example, mutations such as addBook, createUser, resetPassword and refreshFeed are very clear about what the mutation operation will do and on what object.

  2. For better readability, name the mutations with the object first. Some APIs may support similar operations on multiple object types. For example, your API might create objects of types User, Author, and Book. In these cases, it may be more useful to name your mutations with the object’s name first, such as userCreate, authorCreate, and authorCreate.

  3. Use more specific names for actions. For example, let’s say that your application needs to reset and send the password for a particular email account. Naming the operation sendResetPasswordEmail is more intuitive and specific than sendEmail(type: PASSWORD_RESET). The latter choice could imply multiple mutations named sendEmail with varying variable definitions. With more specificity in the naming convention, it becomes easier for a UI developer to build the frontend because they will know exactly which mutation to run. Backend developers can also optimize their APIs by only providing a specific subset of mutations that do only certain things. Because of this design, attackers will have a much harder time exploiting the API than if the mutations were named more generically.

  4. Try to ensure that your mutation uses a single argument object. Rather than have multiple individual objects, create a single object that is nested as much as possible to include all the required fields.
    Updating the addBook mutation to use a single nested input object should look like this:

mutation AddNewBook (input: AddBookInput!) { 
  addBook($input: $input) {
    title
    author
  }
} 
Enter fullscreen mode Exit fullscreen mode

The underlying title and author fields are wrapped in a single AddBookInput object type instead of being individually specified.

type AddBookInput {
  title: String!
author: author
 # Additional fields can be added to this single object.
} 
Enter fullscreen mode Exit fullscreen mode

As a result, any existing schema design can be easily extended by adding more nested subfields to the input object rather than updating the whole structure of the mutation to include more fields. Similarly, you can deprecate unnecessary fields in your input object by simply removing the nested subfield.

Conclusion

In a GraphQL API, queries are used to fetch data from a server, while mutations are used to modify or write server-side data. Therefore, it is essential to understand its structure when defining a mutation, including the type definition, the mutation name, and variable definitions. For better results when using mutations, keep in mind some of the best practices discussed in this blog.

While GraphQL has been welcome competition for REST, it’s critical that developers can use GraphQL to query and mutate data from their databases. Fauna is a flexible, developer-friendly, serverless database delivered as a secure and scalable cloud API with a native GraphQL interface. It’s free to try and takes minutes to spin up a new database.

Sign-up for free
The data API for modern applications is here. Sign-up for free without a credit card and get started instantly.
Sign-up now

Quick start guide
Try our quick start guide to get up and running with your first Fauna database, in only 5 minutes!
Read more

Top comments (0)

πŸ‘€ Every week new members join DEV and share a bit about them in our Welcome Thread

Welcome them to DEV and share a bit about yourself.