Mastering GraphQL Mutations: A Step-by-Step Guide for Beginners

Shivam DubeyShivam Dubey
3 min read

GraphQL mutations allow clients to modify data on the server, such as creating, updating, or deleting records. Unlike queries, which fetch data, mutations are used to perform side effects on the server.

This article introduces basic mutations and explains how to handle input types in a GraphQL API.


What Are GraphQL Mutations?

In GraphQL, mutations allow you to:

  1. Change data on the server.

  2. Optionally return updated data as part of the response.

Example:

Here’s a basic mutation:

mutation {
  createUser(input: {name: "Alice", age: 25}) {
    id
    name
    age
  }
}

This mutation:

  • Creates a user with the provided input fields (name and age).

  • Returns the newly created user’s data (id, name, and age).


Writing Basic Mutations

Step 1: Define the Schema

Define the mutation in the schema along with the required input type.

# schema.graphql
type User {
    id: ID!
    name: String!
    age: Int
}

input CreateUserInput {
    name: String!
    age: Int!
}

type Mutation {
    createUser(input: CreateUserInput!): User!
}

Explanation:

  • User: Represents the user object returned after the mutation.

  • CreateUserInput: An input type defining the fields required to create a new user.

  • Mutation: The root type for all mutations. The createUser mutation accepts an input object and returns a User.


Step 2: Implement the Resolver

Create a resolver for the createUser mutation.

package main

import (
    "fmt"
    "github.com/google/uuid"
)

type User struct {
    ID   string
    Name string
    Age  int
}

var users = []*User{}

type mutationResolver struct{}

func (r *mutationResolver) CreateUser(input CreateUserInput) (*User, error) {
    user := &User{
        ID:   uuid.NewString(), // Generate a unique ID
        Name: input.Name,
        Age:  input.Age,
    }
    users = append(users, user) // Add the new user to the list
    return user, nil
}

type CreateUserInput struct {
    Name string
    Age  int
}

Explanation:

  1. User Struct: Represents the User data type.

  2. Global User List: Simulates a database by storing users in memory.

  3. UUID Generation: Generates a unique ID for each new user using uuid.NewString().

  4. Resolver Logic:

    • Takes the input object (CreateUserInput).

    • Creates a new user object.

    • Appends it to the users list.

    • Returns the newly created user.


Step 3: Mutation Example

You can test the mutation with the following query in GraphQL Playground:

mutation {
  createUser(input: {name: "Alice", age: 25}) {
    id
    name
    age
  }
}

Expected Output:

{
  "data": {
    "createUser": {
      "id": "a1b2c3d4-e5f6-7890-ghij-klmnopqrst",
      "name": "Alice",
      "age": 25
    }
  }
}

Handling Input Types

Input types allow you to define structured data for mutations. Let’s dive into more details:


Step 1: Update the Schema for Input Types

We already defined the input type CreateUserInput in the schema:

input CreateUserInput {
    name: String!
    age: Int!
}

Explanation:

  • Input Object: Defines the shape of data the mutation expects.

  • Mandatory Fields: The ! ensures name and age must be provided.


Step 2: Pass Input in Resolvers

The input type is automatically mapped to Go structs:

type CreateUserInput struct {
    Name string
    Age  int
}

func (r *mutationResolver) CreateUser(input CreateUserInput) (*User, error) {
    user := &User{
        ID:   uuid.NewString(),
        Name: input.Name,
        Age:  input.Age,
    }
    users = append(users, user)
    return user, nil
}

Explanation:

  • Struct Mapping: The input parameter matches the CreateUserInput schema.

  • Reusability: You can use the CreateUserInput struct in multiple resolvers.


Testing Additional Scenarios

  1. Create Multiple Users:

     mutation {
       createUser(input: {name: "Bob", age: 30}) {
         id
         name
         age
       }
     }
    
  2. Query All Users (if supported):

     query {
       users {
         id
         name
         age
       }
     }
    

Conclusion

This article explained:

  • How to write basic mutations.

  • How to define and handle input types for structured data.

GraphQL mutations provide a powerful way to modify server-side data while maintaining flexibility. By using input types, you can enforce structured inputs and reduce validation overhead. Start experimenting with these concepts to master creating efficient APIs!

0
Subscribe to my newsletter

Read articles from Shivam Dubey directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Shivam Dubey
Shivam Dubey