Writes in GraphQL follow conventions that keep clients' caches up to date.
A mutation is an operation whose root fields change data. Syntactically it is just like a query, but it starts with mutation and its fields live on the Mutation root type.
type Mutation {
addBook(input: AddBookInput!): AddBookPayload!
}
input AddBookInput { title: String! authorId: ID! }
type AddBookPayload { book: Book errors: [UserError!]! }Conventions worth adopting:
- Name mutations as verbs for specific actions (
publishPost,cancelOrder) rather than genericupdatePostwith dozens of optional fields. - Take a single
inputargument of an input type, so you can add fields later without breaking callers. - Return a payload type containing the changed objects. The client selects fields on them, and normalized caches (Apollo, urql, Relay) update automatically when the returned object has an
id. - Model expected business failures (validation, "out of stock") as data in the payload, and keep the
errorsarray for unexpected failures.
One execution rule differs from queries: when a document has several root mutation fields, they run serially in order, while query root fields may run in parallel.
Example
mutation AddBook($input: AddBookInput!) {
addBook(input: $input) {
book { id title } # read back what changed
errors { field message } # expected failures as data
}
}
# variables:
# { "input": { "title": "Kindred", "authorId": "a7" } }
#
# response:
# { "data": { "addBook": {
# "book": { "id": "b19", "title": "Kindred" },
# "errors": [] } } }Remember
- Mutation root fields run serially, in document order
- One
inputargument plus a payload type is the common convention - Return changed objects with ids so client caches update
Watch out for
- Returning only a Boolean, forcing the client to refetch what changed.