Writing GraphQL Mutations

1. Defining Mutation Fields

ConventionDetail
NamingverbNoun: createPost, archiveOrder
ArgsSingle input object
ReturnPayload type with mutated entity + errors
SequentialTop-level mutation fields execute serially

2. Using Mutation Arguments

Example: Single input arg

type Mutation {
  updatePost(input: UpdatePostInput!): UpdatePostPayload!
}

input UpdatePostInput { id: ID!, title: String, body: String }

3. Returning Mutation Payload

FieldPurpose
Mutated entitypost: Post
User errorserrors: [UserError!]!
Cursor / clientMutationIdRelay-style traceability
Side datae.g. order: Order after canceling

4. Creating Records

Example: Create mutation

Mutation: {
  createPost: async (_, { input }, { db, user }) => {
    if (!user) throw new GraphQLError("Unauthenticated");
    const post = await db.post.create({ data: { ...input, authorId: user.id } });
    return { post, errors: [] };
  }
}

5. Updating Records

PatternDetail
PATCH semanticsOmitted = unchanged, null = clear
Optimistic concurrencyInclude version in input
AuthorizationVerify ownership before update

6. Deleting Records

Example: Delete returns id

type DeletePostPayload { deletedId: ID, errors: [UserError!]! }

type Mutation { deletePost(id: ID!): DeletePostPayload! }

7. Using Input Objects

ReasonBenefit
Forward compatibilityAdd fields without breaking signature
ReusabilitySame input for create/upsert
ValidationCentralized via custom scalars/directives

8. Returning Updated Data

Example: Return entity to update cache

mutation {
  updatePost(input: { id: "1", title: "New" }) {
    post { id title updatedAt }
    errors { field message }
  }
}

9. Handling Mutation Errors

Error KindStrategy
Validation (field-level)Return in errors array (typed)
AuthorizationThrow GraphQLError with code
Not foundThrow with NOT_FOUND code
System failureThrow; mask details via formatError

10. Using Mutation Variables

Example: Mutation with variables

mutation CreatePost($input: CreatePostInput!) {
  createPost(input: $input) {
    post { id }
    errors { message }
  }
}

11. Batching Mutations

ApproachDetail
Multiple root fields (aliased)Server runs serially
Single batch mutationcreatePosts(inputs: [CreatePostInput!]!)
TransactionalWrap server-side in DB transaction

12. Implementing Optimistic Updates

Example: Apollo optimistic response

useMutation(LIKE_POST, {
  optimisticResponse: ({ id }) => ({
    likePost: { __typename: "LikePostPayload", post: { __typename: "Post", id, liked: true, likeCount: 1 + current } }
  })
});