Labs ICT
Pro Login

Mutations in GraphQL

Mutations in GraphQL

While queries are for reading data, mutations are for writing data. They let you create, update, and delete information on the server.

Mutations work similarly to queries, but they always execute on the server in the order they're received.

Basic Mutations

A mutation defines operations that change data. You call a mutation just like a query, but using the mutation keyword.

// Schema
type Mutation {
  createUser(name: String!, email: String!): User!
  updateUser(id: ID!, name: String): User!
  deleteUser(id: ID!): Boolean!
}

// Create a user
mutation {
  createUser(name: "Charlie", email: "charlie@example.com") {
    id
    name
    email
  }
}

// Response
{
  "data": {
    "createUser": {
      "id": "3",
      "name": "Charlie",
      "email": "charlie@example.com"
    }
  }
}

Notice that the mutation returns the created user. This is a common pattern - mutations return the modified data so the client can update its cache.

Using Input Types

For mutations with many parameters, use input types to keep things organized:

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

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

// Mutation with input type
mutation {
  createUser(input: {
    name: "Diana"
    email: "diana@example.com"
    age: 28
    bio: "Software developer"
  }) {
    id
    name
  }
}

Input types make mutations cleaner and easier to maintain. You can add new fields without changing the mutation signature.

Returning Data from Mutations

There are two common patterns for what mutations return:

Return the modified object: Best for updates where you need the new state.

// Returns the updated user
type Mutation {
  updateUser(id: ID!, input: UpdateUserInput!): User!
}

mutation {
  updateUser(id: "1", input: { name: "Alice Updated" }) {
    id
    name
    email
  }
}

Return a success flag: Simple operations that don't need to return data.

// Returns success boolean
type Mutation {
  deleteUser(id: ID!): Boolean!
}

mutation {
  deleteUser(id: "1")
}

// Response
{
  "data": {
    "deleteUser": true
  }
}

Multiple Operations in One Request

You can combine multiple mutations in a single request. They execute sequentially:

mutation {
  createUser(input: { name: "Eve", email: "eve@example.com" }) {
    id
    name
  }
  createPost(input: { title: "First Post", content: "Hello world" }) {
    id
    title
  }
}

However, be careful with this pattern. If one mutation fails, the others may have already executed. For atomic operations, consider using a single mutation that handles multiple changes.

Error Handling in Mutations

GraphQL has a unique approach to error handling. Even when errors occur, the response still has a 200 status code. Errors are returned in an errors array alongside the data.

// Failed mutation
mutation {
  createUser(input: { name: "", email: "invalid" }) {
    id
  }
}

// Response with errors
{
  "data": {
    "createUser": null
  },
  "errors": [
    {
      "message": "Name is required",
      "path": ["createUser"],
      "extensions": {
        "code": "VALIDATION_ERROR"
      }
    }
  ]
}

Always check for errors in your client code and handle them gracefully.