Change data with mutations — define them on the Mutation type, accept structured data with input types, return the updated object, and run several in one request.
Why: queries read; mutations write. Mutation is the second root type, and its fields are the write operations clients may perform — create, update, delete. By convention each mutation returns the object it changed, so the client gets fresh data back in the same round-trip.
type Mutation {
addBook(title: String!, author: String!): Book!
deleteBook(id: ID!): Boolean!
}Why: a mutation resolver lives under Mutation and works like any resolver — read args, change data, return the result. Here addBook creates a record and returns it, so the client can render the new book immediately.
const resolvers = {
Mutation: {
addBook: (_p, { title, author }) => {
const book = { id: String(books.length + 1), title, author }
books.push(book)
return book
},
},
}Why: calling a mutation looks like a query but starts with the mutation keyword. You pass arguments (via variables) and select which fields of the result you want back — the same field-selection rule as queries.
mutation AddBook($title: String!, $author: String!) {
addBook(title: $title, author: $author) {
id
title
}
}
# Variables: { "title": "Hyperion", "author": "Simmons" }Why: a create or update often has many fields. Bundle them into an input type so the signature stays clean and reusable. Note: a key rule — mutations in a single request run in SERIES, top to bottom (unlike query fields, which run in parallel), so order is predictable.
input AddBookInput {
title: String!
author: String!
pages: Int
}
type Mutation {
addBook(input: AddBookInput!): Book!
}