In Part 2 of Understanding GraphQL for Beginners, we created queries that fetched us data. In Part 3, we learn what are mutations and create mutation queries to modify data. Before we begin, let’s recap what we learned so far in this series:
- GraphQL is a data manipulation and query language for APIs.
- There’s no more over and under fetching of data.
- Root fields define the structure of your response fields based on the object fields selected. They’re the entry points (similar to endpoints) to your GraphQL server.
- Object fields are attributes of an object.
Learning Outcomes
- Create a GraphQL mutation to modify data in the database.
- Use a GraphQL mutation to create a new ActiveRecord.
- Modify data using a GraphQL mutation.
- Use a GraphQL mutation to destroy an existing ActiveRecord.
Before You Start
We’re using the same repository as Part 2 for this tutorial. As a reminder, the repository is set up with models and gems needed for GraphQL.
The following models are
Food
Attribute | Type |
id | Bigint |
name | String |
place_of_origin | String |
image | String |
created_at | Timestamp |
updated_at | Timestamp |
Nutrition
Attribute | Type |
id | Bigint |
food_id | Bigint |
serving_size | String |
calories | String |
total_fat | String |
trans_fat | String |
saturated_fat | String |
cholesterol | String |
sodium | String |
potassium | String |
total_carbohydrate | String |
dietary_fiber | String |
sugars | String |
protein | String |
vitamin_a | String |
vitamin_c | String |
calcium | String |
iron | String |
created_at | Timestamp |
updated_at | Timestamp |
What Are Mutations?
Mutations are queries that create, modify, or delete objects in your database, similar to a PUT, POST, or DELETE request in REST. Mutation requests are sent to the same endpoint as query requests. Mutation queries have the following structure:
- The query starts with mutation.
- Any required arguments are under input.
- The mutation field name contains the action it’s trying to perform, that is
foodCreate
.
We’re naming our mutation query with an object first, followed by the action. This is useful to order the mutations alphabetically, as seen below.
Ordered by object first | Ordered by action first |
food_create.rb | create_food.rb |
food_delete.rb | create_nutrition.rb |
fiid_update.rb | delete_food.rb |
nutrition_create.rb | delete_nutrition.rb |
nutrition_delete.rb | update_food.rb |
nutrition_update.rb | update_nutrition.rb |
Left: Naming a mutation with an object first, followed by the action. Right: Naming a mutation with the action first, followed by an object.
There’s no preference with which naming convention you go with, but, we’re using the naming convention used on the left side of the image. You can find all mutations under the mutations
directory and mutation_type.rb
under the types
directory.
Creating Your First Mutation Query
We create a foodCreate
mutation to create new food items. To create a mutation query, enter the following in your terminal: rails g graphql:mutation foodCreate
The rails generator
does the following things:
- Check if a new mutation file like
base_mutation.rb
andmutation_type.rb
exists. If it doesn’t, create them. - Add the root field,
food_create
tomutation_type.rb
. - Create a class called
food_create.rb
.
Let’s go to the mutation_type.rb class and remove the field and method called test_field
.
Notice how we don’t need to write a method here like in query_type.rb. The mutation: Mutations::foodCreate executes a method called resolve
in that mutation class. We’ll learn what the resolve
method is soon. We then go to food_create.rb and your class looks like this:
The first thing we’re going to do is add input arguments. Remove all the comments, then add the following:
GraphQL uses camel case ( placeOfOrigin) by default. To be consistent with Shopify’s style guide, we’ll use a snake case (place_of_origin) instead. The field’s snake case is converted to camel case by GraphQL automatically!
Next, we need to add in a resolve method. The resolve method fetches the data for its field (food_create
from mutation_type.rb
) and returns a response back. GraphQL server only has one resolve method per field in its schema.
You might be wondering what **
is. It’s an operator called a double splat and it passes in a hash to the resolve method. This allows us to pass in as many arguments as we like. For best practices, if there are more than three parameters, use a double splat to pass in the parameters. For the sake of simplicity, we use the double splat for the three arguments.
We then add in type Types::FoodCreate
to indicate our response fields, and inside the resolve method, create a new ActiveRecord.
Now, let’s go test it out on GraphiQL! Go to http://localhost:3000/graphiql to test out our new mutation query!
Write the following query:
When you execute the query, you get the following response.
Try It Yourself #1
Create a mutation called nutritionCreate
to create a new Nutrition ActiveRecord. As there are a lot of attributes for the Nutrition class, copy the input arguments from this gist: https://gist.github.com/ShopifyEng/bc31c9fc0cc13b9d7be04368113b49d4
If you want to see the solution, check out nutrition_create.rb as well as its query and response.
Creating the Mutation to Update an Existing Food Item
Create a new mutation called foodUpdate using rails g graphql:mutation foodUpdate
. Inside the food_update class, we need to add in the arguments to update. ID
will be part of the arguments.
The only required argument here is ID
. We need to use an ID
to look for an existing product. This allows the resolve method to find the food item and update it.
Next, we write the resolve method and response back.
Let’s test this new mutation query out. We rename our new food item from Apple Pie to Pumpkin Pie.
Try It Yourself #2
Create a mutation called nutritionUpdate to update an existing Nutrition ActiveRecord.
As there are a lot of attributes for the Nutrition class, copy the input arguments from this gist: https://gist.github.com/ShopifyEng/406065ab6c6ce68da6f3a2918ffbeaab.
If you would like to see the solution, check out nutrition_update.rb and the query as well as its query and response.
Creating the Mutation to Delete an Existing Food Item
Create a new mutation called foodDelete using rails g graphql:mutation foodDelete
. The only argument needed in food_delete.rb
is ID.
Next, we need to add the return type and the resolve method. For simplicity, we just use Types::FoodType
as the response.
Let’s test this mutation in GraphiQL.
Try It Yourself #3
Create a mutation called nutritionDelete to delete an existing Nutrition ActiveRecord. Similar to foodDelete, we use Types::NutritionType
as the response.
If you would like to see the solution, check out nutrition_delete.rb as well as its query and response.
We’ve reached the end of this tutorial, I hope you enjoyed creating mutations to modify, create or delete data.
Let’s recap what we learned!
- Mutations are queries that create, modify, or delete objects in your database.
- To generate a mutation, we use
rails g graphql:mutation nameAction
. - A resolve method fetches the data for its field (food_create from mutation_type.rb) and returns a response back.
- GraphQL server only has one resolve method per field in its schema.
GraphQL is a powerful data manipulation and query language for APIs that offers lots of flexibility. At times, it can seem very daunting to implement GraphQL to your application. I hope the Understanding GraphQL for Beginners series helps you to implement GraphQL into your personal projects or convince your work to adopt the GraphQL ecosystem.
If you would like to see the finished code for part three, check out the branch called part-3-solution.
Often mistaken as an intern Raymond Chung is building a new generation of software developers through Shopify’s Dev Degree program. As a Technical Educator, his passion for computer science and education allows him to create bite-sized content that engages interns throughout their day-to-day. When he is not teaching, you’ll find Raymond exploring for the best bubble tea shop.
If building systems from the ground up to solve real-world problems interests you, our Engineering blog has stories about other challenges we have encountered. Visit our Engineering career page to find out about our open positions. Join our remote team and work (almost) anywhere. Learn about how we’re hiring to design the future together - a future that is digital by default.