Setting the file. One moment.
Subchapter 10.2
references/mutations.mdMarkdown8 KBView on GitHub
This reference covers patterns for writing effective GraphQL mutations.
mutation CreatePost($input: CreatePostInput!) {
createPost(input: $input) {
id
title
createdAt
}
}Variables:
{
"input": {
"title": "My Post",
"content": "Post content..."
}
}mutation UpdatePost($id: ID!, $input: UpdatePostInput!) {
updatePost(id: $id, input: $input) {
id
title
updatedAt
}
}Execute multiple mutations in one request (sequential execution):
mutation SetupUserProfile($userId: ID!, $profileInput: ProfileInput!, $settingsInput: SettingsInput!) {
updateProfile(userId: $userId, input: $profileInput) {
id
bio
}
updateSettings(userId: $userId, input: $settingsInput) {
id
theme
notifications
}
}Recommended pattern - single input argument:
mutation CreateUser($input: CreateUserInput!) {
createUser(input: $input) {
id
email
}
}{
"input": {
"email": "user@example.com",
"name": "John Doe",
"password": "secret123"
}
}mutation CreateOrder($input: CreateOrderInput!) {
createOrder(input: $input) {
id
total
}
}{
"input": {
"items": [
{ "productId": "prod_1", "quantity": 2 },
{ "productId": "prod_2", "quantity": 1 }
],
"shippingAddress": {
"street": "123 Main St",
"city": "New York",
"country": "US"
}
}
}mutation UpdateUser($id: ID!, $input: UpdateUserInput!) {
updateUser(id: $id, input: $input) {
id
name
bio
}
}{
"id": "user_123",
"input": {
"name": "New Name"
// bio not included - won't be changed
}
}Always return the mutated object with updated fields:
mutation UpdatePost($id: ID!, $input: UpdatePostInput!) {
updatePost(id: $id, input: $input) {
id
title
content
updatedAt # Server-set field
}
}If mutation affects related data, include it:
mutation AddComment($input: AddCommentInput!) {
addComment(input: $input) {
id
body
post {
id
commentCount # Updated count
}
author {
id
name
}
}
}Select fields needed to update your cache:
mutation DeletePost($id: ID!) {
deletePost(id: $id) {
id # Needed to remove from cache
author {
id
postCount # May need to decrement
}
}
}mutation CreatePost($input: CreatePostInput!) {
createPost(input: $input) {
id
title
createdAt
author {
id
posts(first: 1) {
edges {
node {
id
}
}
totalCount
}
}
}
}When schema uses union types for errors:
mutation CreateUser($input: CreateUserInput!) {
createUser(input: $input) {
... on CreateUserSuccess {
user {
id
email
}
}
... on ValidationError {
message
field
}
... on EmailAlreadyExists {
message
existingUserId
}
}
}const result = await client.mutate({
mutation: CREATE_USER,
variables: { input },
});
const { createUser } = result.data;
switch (createUser.__typename) {
case "CreateUserSuccess":
// Handle success
return createUser.user;
case "ValidationError":
// Handle validation error
throw new ValidationError(createUser.field, createUser.message);
case "EmailAlreadyExists":
// Handle specific business error
throw new EmailExistsError(createUser.existingUserId);
}Handle network and GraphQL errors:
try {
const result = await client.mutate({
mutation: CREATE_POST,
variables: { input },
});
return result.data.createPost;
} catch (error) {
if (error.graphQLErrors?.length) {
// Handle GraphQL errors
const gqlError = error.graphQLErrors[0];
if (gqlError.extensions?.code === "UNAUTHENTICATED") {
// Redirect to login
}
}
if (error.networkError) {
// Handle network error
}
throw error;
}Include all fields that will display immediately:
mutation LikePost($postId: ID!) {
likePost(postId: $postId) {
id
likeCount
isLikedByViewer
}
}client.mutate({
mutation: LIKE_POST,
variables: { postId: "post_123" },
optimisticResponse: {
likePost: {
__typename: "Post",
id: "post_123",
likeCount: currentCount + 1,
isLikedByViewer: true,
},
},
});For create mutations, use temporary IDs:
mutation AddComment($input: AddCommentInput!) {
addComment(input: $input) {
id
body
createdAt
author {
id
name
avatarUrl
}
}
}client.mutate({
mutation: ADD_COMMENT,
variables: { input: { postId, body } },
optimisticResponse: {
addComment: {
__typename: "Comment",
id: `temp-${Date.now()}`, // Temporary ID
body,
createdAt: new Date().toISOString(),
author: {
__typename: "User",
id: currentUser.id,
name: currentUser.name,
avatarUrl: currentUser.avatarUrl,
},
},
},
});| Operation | Pattern | Examples |
|---|---|---|
| Create | Create{Type} | CreateUser, CreatePost |
| Update | Update{Type} | UpdateUser, UpdatePost |
| Delete | Delete{Type} | DeleteUser, DeletePost |
| Action | {Verb}{Type} | PublishPost, ArchiveProject |
| Relationship | {Add/Remove}{Type} | AddTeamMember, RemoveTag |
mutation CreateUser($input: CreateUserInput!) { ... }
mutation UpdateUserProfile($userId: ID!, $input: ProfileInput!) { ... }
mutation DeletePost($id: ID!) { ... }
mutation PublishArticle($id: ID!) { ... }
mutation ArchiveProject($id: ID!) { ... }
mutation AddItemToCart($input: AddItemInput!) { ... }
mutation RemoveTeamMember($teamId: ID!, $userId: ID!) { ... }
mutation FollowUser($userId: ID!) { ... }
mutation MarkNotificationAsRead($id: ID!) { ... }Match client operation name to server mutation:
# Server schema
type Mutation {
createPost(input: CreatePostInput!): Post!
}
# Client operation - name reflects the action
mutation CreatePost($input: CreatePostInput!) {
createPost(input: $input) {
id
title
}
}Add context when same mutation is used differently:
# For creating a draft
mutation CreateDraftPost($input: CreatePostInput!) {
createPost(input: $input) {
id
title
status
}
}
# For creating and publishing immediately
mutation CreateAndPublishPost($input: CreatePostInput!) {
createPost(input: $input) {
id
title
status
publishedAt
}
}