Mutations: Writing Data with GraphQL

In the previous installment of this series, we built queries that read data from a GraphQL API. This part covers the write side: mutations, which create, update, or delete records in the database. If you're following along, you'll want the same food-app repository used in Part 2, which already includes the necessary models and GraphQL gems. The relevant models are Food and Nutrition.

How Mutations Differ from Queries

A mutation is a query that changes data, analogous to PUT, POST, or DELETE requests in REST. Mutation requests hit the same endpoint as standard queries, but their structure is distinct:

  • The query begins with the keyword mutation.
  • Required arguments are grouped under an input field.
  • The mutation field itself names the action, such as foodCreate.
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

There's no hard rule for naming mutations, but putting the object first (e.g., foodCreate) keeps related operations grouped alphabetically in your schema. All mutation classes live in the mutations directory, with their root fields registered in mutation_type.rb under the types directory.

Generating and Structuring a Mutation

To create a new mutation that adds food items, run the Rails generator:

rails g graphql:mutation foodCreate

This generator performs three tasks:

  1. Creates base_mutation.rb and mutation_type.rb if they don't already exist.
  2. Adds a food_create root field to mutation_type.rb.
  3. Generates a food_create.rb class file.

In mutation_type.rb, you should remove the generated test_field entry. Unlike the query type, you don't write a resolver method here—the mutation: Mutations::foodCreate declaration automatically invokes a resolve method defined inside the mutation class itself.

In food_create.rb, start by declaring the input arguments, replacing the generated comments. GraphQL defaults to camel case (e.g., placeOfOrigin), but you can declare snake case arguments (e.g., place_of_origin) and GraphQL will convert them automatically.

The resolve method is the workhorse: it fetches or manipulates data for its field (food_create) and returns a response. Each field in a GraphQL schema has exactly one resolve method. The double splat operator (**) passes a hash of arguments into resolve, which is the recommended pattern when you have more than three parameters—though it works fine here for three as well.

Declare the return type as Types::FoodCreate to define the response shape, then instantiate a new ActiveRecord inside resolve. Head to http://localhost:3000/graphiql to execute the mutation and see the created record returned.

Exercise: Add a Nutrition Create Mutation

Try building a nutritionCreate mutation for the Nutrition model. Since it has many attributes, copy the input arguments from this gist. The finished nutrition_create.rb, along with a sample query and response, is available in the solution gist.

Updating Existing Records

Generate an update mutation with rails g graphql:mutation foodUpdate. In food_update.rb, the arguments include an ID—the only required one—so the resolver can locate the target record before applying updates.

The resolve method finds the food item by ID, updates its attributes with the provided input, and returns the modified object. A quick GraphiQL test confirms the change, such as renaming an existing item from Apple Pie to Pumpkin Pie.

Exercise: Add a Nutrition Update Mutation

Create nutritionUpdate to modify an existing Nutrition record. Pull the input arguments from this gist. The solution, including the query and response, is in nutrition_update.rb.

Deleting Records via Mutation

Deleting is similar but simpler. Run rails g graphql:mutation foodDelete. The only argument needed in food_delete.rb is the ID of the record to remove. For the return type, you can use the existing Types::FoodType for simplicity, then implement the resolve method to find and destroy the record.

Exercise: Add a Nutrition Delete Mutation

Following the foodDelete pattern, create nutritionDelete using Types::NutritionType as the response type. Check the nutrition_delete.rb solution and its query and response.

Key Takeaways

  • Mutations handle create, update, and delete operations in GraphQL.
  • Generate them with rails g graphql:mutation nameAction.
  • The resolve method within a mutation class performs the data operation and returns a response; each schema field has exactly one resolve method.

The complete solution for this tutorial is available on the part-3-solution branch of the repository.