Mutation
mutation OperationName($input: InputType!) { mutationField(input: $input) { id field } }
A GraphQL mutation is a write operation that modifies server-side data. Mutations execute serially – if a document contains multiple mutation fields, they run one after another in declaration order, not in parallel. This guarantees that a mutation that increments a counter followed by a mutation that reads it sees the updated value. Mutations return data just like queries – clients specify the fields they want back in the response.
How it works
Mutations are how GraphQL APIs accept writes. They mirror POST/PUT/PATCH/DELETE semantics in REST, but all writes go through the mutation operation type.
Serial execution: the GraphQL spec §6.3.1 requires that mutation fields at the top level execute serially. This is a hard guarantee – parallel execution of mutations would produce race conditions for sequential operations like 'increment and then read'. Fields nested inside a mutation may still be fetched in parallel.
Input types: mutation arguments typically use Input Object types (keyword input in SDL) rather than regular Object types. This is because Object types can have circular references and resolvers, which input types cannot. Defining a dedicated input type per mutation makes the API explicit and extensible.
Return type design: mutations should return the modified resource, not just a boolean. This allows the client to update its cache without a separate fetch. The Relay specification recommends every mutation return a dedicated payload type with a clientMutationId echo field.
Optimistic updates: because mutations return the modified data, clients (Apollo, urql) can apply optimistic updates immediately and then reconcile with the real server response.
Idempotency: GraphQL does not enforce idempotency on mutations. Design your mutation semantics (createOrUpdate vs create) to match your API contract.
Examples
mutation CreatePost($input: CreatePostInput!) {
createPost(input: $input) {
id
title
author {
name
}
createdAt
}
}mutation UpdateUser($id: ID!, $input: UpdateUserInput!) {
updateUser(id: $id, input: $input) {
id
name
email
updatedAt
}
}mutation DeletePost($id: ID!) {
deletePost(id: $id) {
success
deletedId
}
}mutation BatchOps {
first: incrementCounter(by: 1) { value }
second: incrementCounter(by: 2) { value }
# first runs to completion before second starts
}mutation Login($email: String!, $password: String!) {
login(email: $email, password: $password) {
token
expiresAt
user { id name }
}
}Spec Rules
Top-level mutation fields execute serially – this is a spec guarantee, not an implementation detail (§6.3.1)
Nested fields within a mutation result may be fetched in parallel
Use Input Object types for mutation arguments – regular Object types are not allowed as input
Return the modified resource, not just a boolean – enables client cache updates without a second fetch
Never put mutation logic in a query resolver – side effects in queries violate the spec's intent
Multiple mutations in one document require operationName in the request to select which to run