Skip to content
API Design Learn/GraphQL Foundations
Browsing as a guest. Sign in to save your progress and earn XP as you complete chapters.

Mutations Done Right

6 min read

You'll learn to

  • -Design GraphQL mutations with dedicated input types and a consistent naming convention
  • -Return enough information from a mutation for the client to update its local state without a follow-up query

Mutations are GraphQL's equivalent of REST's POST/PUT/PATCH/DELETE - the mechanism for changing data rather than just reading it. Like queries, mutations are defined on their own root type, and a few conventions separate mutations that are pleasant to use from ones that fight every client that calls them.

Input Types, Not Raw Arguments

A dedicated input type instead of a long, unstructured argument list
input CreateUserInput {
  name: String!
  email: String!
  age: Int
}

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

A mutation could accept each field as a separate top-level argument (`createUser(name: String!, email: String!, age: Int): User!`), but a dedicated `input` type keeps the mutation's signature stable as fields are added - a new optional field on the input type doesn't change the mutation's own signature, whereas adding a new top-level argument does. This is the GraphQL equivalent of the DTO discipline from the REST modules: a deliberate, versioned boundary instead of the internal shape leaking directly into the call site.

Naming: Verb-Noun, Consistently

A consistent verb-noun naming convention across every mutation
type Mutation {
  createUser(input: CreateUserInput!): User!
  updateUser(id: ID!, input: UpdateUserInput!): User!
  deleteUser(id: ID!): DeleteUserPayload!
}

Just as REST benefits from consistent resource naming, GraphQL mutations benefit from a consistent verb-noun pattern (`createX`, `updateX`, `deleteX`) applied uniformly - a client integrating against the API can predict a mutation's name and shape from the pattern, instead of needing to look up each one individually because naming varies field by field.

Returning Enough to Avoid a Follow-Up Query

A mutation payload that gives the client what it needs to update local state
type DeleteUserPayload {
  deletedId: ID!
  success: Boolean!
}

mutation {
  deleteUser(id: "42") {
    deletedId
    success
  }
}

A mutation should return enough information for the client to update its own local cache or UI state without an immediate follow-up query - `createUser` returning the full new `User` object (including its server-assigned `id`) lets the client add it to a local list directly; `deleteUser` returning the deleted ID lets the client remove exactly that item from a local cache, which is why even a delete mutation - conceptually "nothing to return" in REST's 204 sense - typically returns a small payload in GraphQL rather than nothing at all.

This return-enough-for-local-state-updates convention is specifically important for GraphQL clients using a normalized cache (like Apollo Client or Relay) - those caches update automatically based on IDs present in a mutation response, so omitting them forces a wasteful, avoidable follow-up query just to keep the UI in sync.

Interview Signal is part of Pro

See a real weak answer next to a real strong one for this exact topic.

Quiz is part of Pro

Test what you just read with a short quiz, and bank the XP.

Ready to Build This?

Design Mutation Station in the API Design Lab's GraphQL Mastery act.

ScaleDojo Logo
Initializing ScaleDojo