GraphQL Mutations Explained
A practical guide to GraphQL mutations covering create, update, and delete operations, variables, input objects, responses, errors, and common mistakes.
GraphQL mutations are operations used to modify data through a GraphQL API. While queries are normally used to read data, mutations are designed for operations such as creating records, updating existing data, deleting resources, changing statuses, or triggering other server-side actions.
Mutations use the same strongly typed schema system as GraphQL queries. A client specifies the mutation field, provides any required arguments or variables, and selects the fields it wants returned after the operation completes.
This makes GraphQL mutations different from simply sending an arbitrary HTTP request. The mutation name, arguments, input types, and response fields are defined by the GraphQL schema exposed by the server.
What Is a GraphQL Mutation?
A GraphQL mutation is an operation that requests a state-changing action from a GraphQL server. Common examples include creating a user, updating a product, deleting a comment, or changing the status of an order.
mutation {
createUser(name: "Anna", email: "[email protected]") {
id
name
email
}
}The mutation calls createUser with two arguments and asks the server to return the newly created user's id, name, and email.
GraphQL Mutation Syntax
A mutation follows a structure similar to a query, but the operation type is mutation.
mutation {
updateProduct(id: "42", price: 99) {
id
name
price
}
}The mutation operation contains the updateProduct field. Its arguments provide the information required by the server, and the selection set specifies which fields should be returned.
Named GraphQL Mutations
Mutations can have operation names just like queries. Naming important mutations makes larger GraphQL documents easier to understand and debug.
mutation UpdateProduct {
updateProduct(id: "42", price: 99) {
id
name
price
}
}UpdateProduct is the operation name. It identifies the operation document but does not determine which mutation field the server executes.
Creating Data with Mutations
One of the most common uses of mutations is creating new records. The schema might expose fields such as createUser, createProduct, createPost, or another application-specific mutation.
mutation {
createProduct(
name: "Mechanical Keyboard"
price: 120
) {
id
name
price
}
}The server receives the supplied values, performs the requested operation, and returns the fields selected by the client.
Updating Data with Mutations
Mutations can also modify existing records. An update mutation commonly accepts an identifier and one or more values to change.
mutation {
updateProduct(
id: "42"
price: 110
) {
id
name
price
}
}The exact update semantics depend entirely on the server schema. Some APIs expose individual arguments for each editable field, while others use a single input object.
Deleting Data with Mutations
Deletion is another common mutation operation. A delete mutation often accepts an identifier and returns information describing the result.
mutation {
deleteProduct(id: "42") {
id
deleted
}
}The returned fields are defined by the mutation's return type. Some APIs return the deleted object, while others return a status, identifier, or payload containing additional information.
GraphQL Mutation Arguments
Mutation fields can accept arguments in the same general way as query fields. Arguments provide the information required to perform the operation.
mutation {
updateUser(
id: "42"
name: "Anna Smith"
active: true
) {
id
name
active
}
}The available arguments, their names, and their types are determined by the GraphQL schema.
Using Variables in GraphQL Mutations
Variables are commonly used with mutations because mutation input often comes from forms, user actions, or application state. Instead of embedding values directly into the query document, variables are declared separately.
mutation CreateUser($name: String!, $email: String!) {
createUser(
name: $name
email: $email
) {
id
name
email
}
}The variables can then be supplied separately from the mutation document.
{
"name": "Anna",
"email": "[email protected]"
}The exclamation mark in String! means the variable is non-null. The server's schema determines whether a particular variable must be supplied and what values are accepted.
Input Objects in GraphQL Mutations
For mutations with many input values, GraphQL APIs commonly define input object types. This keeps the mutation signature organized and makes structured input easier to validate.
mutation CreateProduct($input: CreateProductInput!) {
createProduct(input: $input) {
id
name
price
}
}The variables can contain an object matching the CreateProductInput type.
{
"input": {
"name": "Mechanical Keyboard",
"price": 120,
"categoryId": "5"
}
}Input types are defined by the schema. Their fields may include required values, optional values, nested input objects, lists, and custom scalar types.
Why Input Objects Are Useful
A mutation with many individual arguments can become difficult to maintain. An input object provides a single structured argument and gives the schema a clear place to define the expected input fields.
mutation UpdateUser($input: UpdateUserInput!) {
updateUser(input: $input) {
id
name
email
active
}
}This pattern is common in production GraphQL APIs, although it is not mandatory. The API designer determines whether a mutation uses individual arguments or an input object.
Selecting Mutation Response Fields
One of the important characteristics of GraphQL mutations is that the client controls which fields should be returned from the mutation payload.
mutation {
createPost(
title: "GraphQL Mutations"
body: "An introduction to mutations."
) {
id
title
}
}The client does not necessarily need to request every field returned by the mutation's type. It can select only the information needed by the application.
Mutation Payloads
Many GraphQL APIs return a dedicated payload object from mutations rather than returning the modified resource directly. The payload can contain the changed object, status information, validation errors, or other fields.
mutation {
createUser(
input: {
name: "Anna"
email: "[email protected]"
}
) {
user {
id
name
email
}
success
}
}The exact payload structure is schema-specific. A mutation may expose fields such as user, success, errors, message, or other application-specific information.
Handling Mutation Errors
GraphQL errors can occur during validation, execution, authorization, input processing, or other server-side operations. A response can contain an errors array alongside data.
{
"data": {
"createUser": null
},
"errors": [
{
"message": "Email is already registered"
}
]
}Some APIs also represent expected business or validation errors inside the mutation payload itself.
mutation CreateUser($input: CreateUserInput!) {
createUser(input: $input) {
user {
id
name
}
errors {
field
message
}
}
}This approach allows the API to distinguish between expected application-level validation results and GraphQL execution errors. The exact design depends on the API.
Mutation Validation
GraphQL validates the structure and types of a mutation against the schema before execution. This can catch problems such as unknown mutation fields, missing required arguments, invalid input fields, and incompatible variable types.
mutation {
createUser(
username: "anna"
) {
id
}
}If createUser does not accept a username argument, the mutation is invalid according to the schema. Schema-aware tools can often identify such problems before the request is sent.
GraphQL Mutation Aliases
Aliases can be used with mutation fields when the same field needs to be selected more than once with different arguments and the server permits the operation.
mutation {
first: updateProduct(id: "1", price: 50) {
id
price
}
second: updateProduct(id: "2", price: 75) {
id
price
}
}The aliases first and second give the two mutation results different response names.
Multiple Mutations in One Operation
A mutation operation can contain multiple root mutation fields when the schema and operation are valid.
mutation {
updateUser(id: "1", active: true) {
id
active
}
updateUser(id: "2", active: true) {
id
active
}
}GraphQL's specification defines serial execution behavior for top-level mutation fields, but applications should still design multi-step mutations carefully. When several changes represent one logical business operation, a dedicated server-side mutation can sometimes provide clearer atomic behavior.
Mutations vs Queries
The primary distinction is their intended operation: queries are used for reading data, while mutations are used for operations that may change server-side state.
| Feature | Query | Mutation |
|---|---|---|
| Primary purpose | Read data | Change data or trigger an action |
| Operation type | query | mutation |
| Typical examples | Get users, list products | Create, update, delete |
| Can request selected fields | Yes | Yes |
| Can use variables | Yes | Yes |
| Can use fragments | Yes | Yes |
| Expected side effects | Normally no | Potentially yes |
Mutations vs REST POST, PUT, PATCH, and DELETE
GraphQL mutations are often compared with REST methods such as POST, PUT, PATCH, and DELETE because all can be used for state-changing operations. However, the models are different.
In REST, the HTTP method and resource endpoint commonly communicate the general operation being performed. In GraphQL, the mutation field defined by the schema communicates the operation, while the GraphQL request is typically sent to the same endpoint used for queries.
| REST Concept | Typical GraphQL Equivalent |
|---|---|
| POST | Create-style mutation |
| PUT | Replace-style mutation |
| PATCH | Partial update mutation |
| DELETE | Delete-style mutation |
These are conceptual comparisons rather than strict one-to-one mappings. A GraphQL schema can define mutation operations with behavior that does not correspond directly to a single REST method.
GraphQL Mutations and HTTP
GraphQL is an application-layer query language and runtime rather than an HTTP method. GraphQL mutations are commonly transported using HTTP POST requests, although transport details depend on the server and client implementation.
POST /graphql
Content-Type: application/json
{
"query": "mutation CreateUser($input: CreateUserInput!) { createUser(input: $input) { id name } }",
"variables": {
"input": {
"name": "Anna"
}
}
}The GraphQL operation is contained in the request body. The HTTP endpoint itself does not normally contain a separate URL for every mutation field.
Mutation Authentication and Authorization
GraphQL mutations often perform sensitive state-changing operations, so authentication and authorization are important parts of the surrounding API design.
A client may authenticate using a session, access token, or another mechanism supported by the application. Authentication establishes who the caller is, while authorization determines whether that caller is allowed to perform the requested mutation.
GraphQL Mutations and Optimistic UI Updates
Frontend applications sometimes update the interface optimistically before a mutation response arrives. For example, a user interface may immediately show a new item as created or mark an item as completed while the server request is still in progress.
If the mutation fails, the client must reconcile the optimistic state with the actual server state. GraphQL clients and application-specific state management libraries can provide different approaches for updating or invalidating cached data after mutations.
Updating Client Caches After Mutations
After a mutation changes server-side data, previously fetched query results may become stale. A frontend application may need to update its local cache, invalidate affected queries, refetch data, or merge the mutation result into the cache.
The correct approach depends on the GraphQL client and its caching model. The mutation response should generally contain enough information for the client to update relevant state efficiently when possible.
Idempotency and GraphQL Mutations
Mutations can have different side-effect characteristics. Repeating a mutation may create another resource, increment a counter again, or otherwise repeat an action. Other mutation designs may be intentionally idempotent.
If a mutation can be retried because of a network failure, the client and server should have a clear strategy for avoiding unintended duplicate effects where necessary. An idempotency key or application-specific request identifier can be used when supported by the API.
Common GraphQL Mutation Mistakes
- Using query instead of mutation for a state-changing operation.
- Requesting a mutation field that is not defined by the schema.
- Forgetting required mutation arguments.
- Using variables with incorrect GraphQL types.
- Sending input fields that do not exist on the input object type.
- Forgetting a selection set when the mutation returns an object type.
- Assuming every API uses the same create, update, and delete mutation names.
- Ignoring validation or business errors returned by the server.
- Assuming an HTTP success status means the mutation itself was completely successful.
- Failing to update or invalidate stale client-side cached data.
- Retrying non-idempotent mutations without considering duplicate side effects.
- Relying on frontend controls instead of server-side authorization.
How to Debug a GraphQL Mutation
When a mutation fails, start by checking the schema and then verify the request step by step.
- Confirm that the mutation field exists in the schema.
- Check the mutation name and spelling.
- Review every required argument.
- Verify variable names and declared types.
- Check the structure of input objects.
- Confirm that enum values use the correct names.
- Verify that the requested response fields exist on the mutation return type.
- Inspect the GraphQL errors array.
- Check application-level errors inside the mutation payload when the API uses them.
- Verify authentication and authorization.
- Test the smallest valid mutation before adding additional fields.
- Check whether the client cache needs to be updated after success.
mutation CreateUser($input: CreateUserInput!) {
createUser(input: $input) {
user {
id
name
}
}
}A minimal mutation like this can be easier to troubleshoot than a large operation containing many fields, fragments, and directives.
Formatting GraphQL Mutations
Good formatting makes mutation arguments and nested response selections easier to inspect.
mutation UpdateOrder($input: UpdateOrderInput!) {
updateOrder(input: $input) {
id
status
customer {
id
name
}
items {
product {
id
name
}
quantity
}
}
}Indenting nested selection sets consistently makes it easier to see which fields belong to the mutation payload and which belong to nested objects.
A Complete GraphQL Mutation Example
The following example combines a named operation, a variable, an input object, a mutation field, nested response fields, and application-level validation errors.
mutation CreateProduct($input: CreateProductInput!) {
createProduct(input: $input) {
product {
id
name
price
category {
id
name
}
}
errors {
field
message
}
}
}{
"input": {
"name": "Mechanical Keyboard",
"price": 120,
"categoryId": "5"
}
}A successful response might contain the created product and an empty errors collection, while a validation failure could return field-specific messages according to the API's payload design.
Best Practices for GraphQL Mutations
- Give important mutation operations descriptive names.
- Use variables instead of embedding dynamic values directly into query strings.
- Use input objects for complex or large mutation inputs when the schema provides them.
- Request only the response fields the client needs.
- Design mutation payloads that provide useful information to clients.
- Handle both GraphQL errors and application-level validation errors.
- Enforce authorization on the server.
- Consider cache updates or query invalidation after successful mutations.
- Use pagination and bounded inputs when mutations can process collections.
- Consider idempotency for operations that may be retried.
- Keep destructive mutations explicit and carefully validated.
- Test mutations with representative valid and invalid inputs.
Frequently Asked Questions
What is a GraphQL mutation?
A GraphQL mutation is an operation intended to change server-side state or trigger an action. Common examples include creating, updating, and deleting records.
What is the difference between a GraphQL query and mutation?
Queries are normally used to read data, while mutations are intended for state-changing operations. Both use the GraphQL schema and allow clients to select the fields they want returned.
Can GraphQL mutations use variables?
Yes. Variables are commonly used with mutations to pass IDs, form values, filters, and structured input separately from the mutation document.
What is a GraphQL input object?
An input object is a schema-defined type used to represent structured input for fields such as mutations. It can contain multiple fields, nested inputs, lists, and other supported GraphQL types.
Can a GraphQL mutation return data?
Yes. A mutation can return an object or payload, and the client selects the fields it wants from that result. APIs often return the created or updated resource along with status or validation information.
Can multiple mutations be sent in one GraphQL operation?
A mutation operation can contain multiple top-level mutation fields when the document is valid. GraphQL specifies serial execution behavior for top-level mutation fields, but applications should still design multi-step operations carefully.
How should GraphQL mutation errors be handled?
Clients should inspect the GraphQL errors array and, when applicable, application-level error fields included in the mutation payload. The exact error model depends on the GraphQL API.
Helpful GraphQL Tools
Several types of GraphQL and HTTP tools can help when developing mutations. Endpoint testers can send mutation operations and variables directly to a GraphQL server. Query formatters can improve the readability of nested mutation documents, while variable formatters can make structured JSON variables easier to inspect. Response formatters can help analyze mutation responses and errors, and general HTTP request builders can be useful when testing the underlying GraphQL request outside an application.
Conclusion
GraphQL mutations provide a structured way to perform state-changing operations through a GraphQL API. They use the same schema-driven approach as queries while adding an operation type specifically intended for changes and actions.
The core pattern is straightforward: choose the mutation field defined by the schema, provide its required arguments or variables, and select the response fields needed by the client. Input objects, payloads, fragments, error handling, authorization, cache updates, and idempotency become increasingly important as mutations grow more complex.