AlgoMaster Logo

Queries and Mutations

High Priority15 min readUpdated September 13, 2026
Listen to this chapter
Unlock Audio

A catalog needs to support both people reading about books and editors changing their details. A bookstore page reads a title, description, and author names, while its catalog editor changes the description and needs to see the saved result. Both interactions use the same book model, but they make different promises: reading should not change business state, while editing requires permission and a clearly defined outcome.

Queries and mutations express these intentions in GraphQL.

This chapter explains how to write reusable operations, send their inputs, select results, and handle writes without confusing execution order with transactions or retry safety.

1. Read and Write Operations

A query reads through the schema's query root. A mutation invokes write operations through its mutation root. Both use selection sets to describe the result the client needs.

This small read schema supports the examples below. It is a self-contained catalog subset; revision is an application-defined version that the service uses to detect concurrent edits.

The root field book identifies a server capability. An operation name such as BookDetails identifies a client-written use of that capability. These names serve different purposes: many named operations can call the same field.

Keep business writes in top-level mutation fields. Query fields and fields nested inside a mutation result must be side-effect-free and idempotent in GraphQL's execution model. Selecting book { title } from a mutation result reads the result; it must not trigger another business change.

The operation type does not follow from the HTTP method alone. A POST request can carry a read-only query or a mutation. Likewise, the JSON request property named query contains the GraphQL document even when that document contains a mutation.

2. Named Queries and Variables

The following operation reads a book and optionally includes its description:

BookDetails is the operation name. $bookId and $withDescription are variables: typed inputs the client supplies separately from the document. id: $bookId passes the variable to the schema field's id argument.

Use variables for values that change between requests. This keeps the operation text stable and avoids constructing GraphQL syntax by concatenating user input. It does not replace validation of business values or safe handling of those values inside backend queries.

A directive changes how GraphQL handles a selection. The built-in @include(if: ...) includes a selection when its Boolean condition is true. @skip(if: ...) excludes it when its condition is true. If both apply to a selection, it appears only when inclusion is true and skipping is false.

Here, omitting withDescription uses its default of false. The response then omits the description key entirely. This differs from selecting the field and receiving null because no description exists. Explicit null is invalid for this non-null Boolean variable.

Assume the server supports the GraphQL over HTTP draft's application/graphql-response+json media type. A client can send a public catalog read as follows:

The successful response contains only the selected fields under data:

no-store is this example's cache policy, not a GraphQL requirement. The draft also allows servers to support GET for queries, with clients encoding the document and variables as URL parameters. Mutations must not execute through GET under that HTTP binding.

Name application operations so logs and tooling can identify their purpose. A document can contain several named operations, but a request executes one selected operation. When it contains more than one, operationName must identify which to run. Sending several definitions does not execute them as a sequence.

3. Aliases and Fragments

A comparison screen may request two books through the same book field. An alias assigns a response key to a field selection, allowing different argument values without a response-name conflict.

A fragment names a reusable selection set. This document uses both features:

left and right become keys under data; the schema field remains book. Without distinct aliases, these two selections with different ID arguments would conflict. If one book is unavailable, its aliased result can be null while the other still returns a book.

...BookCard spreads the fragment into each selection. on Book specifies the type on which those fields apply. Include the fragment definition in the document the client sends to the server; a fragment is not a separately executed request or a reference to a server-stored component.

Fragments help related UI components share field requirements. They do not reduce the amount of data those fields request or make expensive selections free. Review the combined operation, not just each fragment in isolation.

The two root query fields have no guaranteed execution order. The implementation may resolve independent work concurrently, but GraphQL does not promise parallel backend calls or a transactionally consistent snapshot across them.

4. Validation Before Execution

A well-formed document is not necessarily executable. The server checks field selections against the schema and prepares supplied variable values before running the selected operation.

This flow separates request failures from failures that occur after work starts:

Request errors include an unknown field, a missing required variable, or an ambiguous operation choice. A field returning an object needs subfield selections, while a scalar such as title cannot have subfields. Directives do not hide invalid syntax or unknown fields from validation.

For example, changing the book query to request warehouseCost, which is absent from this schema, produces a validation failure. Under the assumed HTTP binding, a representative response is:

The message wording is implementation-specific. There is no data entry because execution did not begin. HTTP status handling differs with some older application/json integrations, so the media type assumption matters.

An execution error is different: some work may already have happened, and the response can contain partial data. Clients need to inspect both data and errors.

5. A Purposeful Mutation

An editor should change a description through an operation that states that intent. The following definitions add a mutation to the catalog schema:

An input groups the write parameters. A payload is the operation's result object. These names and the userErrors pattern are design choices, not required GraphQL conventions.

For this bookstore, authorized editors can update published editions. The server checks permission, validates the proposed value, and compares expectedRevision with the stored revision. It commits an accepted change and increments the revision atomically. The revision check prevents one editor from silently overwriting a change made after their read.

Define the description rule precisely: a supplied string must contain at least one non-whitespace character and at most 2,000 Unicode code points. Store accepted text unchanged. Null clears the description; omission leaves it unchanged. An unchanged value is a successful no-op and does not increment the revision, but still requires permission and a matching revision.

On success, return the saved book with an empty userErrors list. For a known business rejection, return book: null and a non-empty list. Check permission before disclosing whether the target book exists. NOT_FOUND is available only to callers authorized for that catalog scope.

These policies require implementation. The mutation keyword and SDL do not create authorization checks, transactions, revision enforcement, or string-length validation.

6. Selecting the Saved Result

A mutation selects its response fields just as a query does:

Selecting the saved description and revision lets the editor display the authoritative result without immediately issuing another read. The server should return the state this update produced, rather than a later unrelated version fetched after another edit.

The write request carries an editor's credential. The token shown is a placeholder:

If the stored revision is still 7 and the change succeeds, the response is:

Selecting fields from the returned payload only reads the result. The update must not depend on the client requesting book, revision, or any other particular result field.

A stale revision is a business rejection, which this API represents as ordinary typed data. For the same selection, the response body would be:

This response uses HTTP 200 because the GraphQL operation executed and returned its defined result. A whitespace-only description similarly produces INVALID_DESCRIPTION on description, without changing the book. Clients must inspect userErrors; the absence of top-level errors does not imply that the requested write succeeded.

An authenticated caller without edit permission follows a different policy in this example: the field raises an execution error, and no write occurs. A representative body is:

With this nullable mutation field and HTTP binding, that execution result also uses HTTP 200. FORBIDDEN is an application-defined extension code, not a standard GraphQL error code. The HTTP authentication layer can instead reject missing or invalid credentials before GraphQL execution begins.

7. Serial Execution and Write Guarantees

One mutation operation can contain multiple top-level fields. GraphQL executes those fields serially, in collected field order, completing each field's selected result before moving to the next. Nested result fields use normal execution rules.

Suppose an editor submits two aliased updateBookDescription fields for different books. This diagram shows a possible outcome:

The first change remains committed. Serial execution runs the top-level fields in order; it does not automatically roll back earlier changes. A typed business rejection also does not tell GraphQL to stop executing subsequent fields. Execution errors have their own propagation behavior, but they do not undo committed writes either.

The ordering guarantee applies within one operation. It does not serialize other clients' mutation requests. That is why the bookstore still needs an atomic revision check.

Nor can a later field automatically use an ID an earlier field returns: GraphQL prepares variables before execution, and GraphQL cannot automatically pass one field's result as another field's input. Use a purposeful server operation for a dependent workflow, or have the client wait for the first response before sending the next request.

If several changes must succeed together, define one business operation with an explicitly implemented transaction boundary. Do not rely on placing fields next to each other in a mutation document.

8. Retries and Uncertain Outcomes

A mutation response can fail after the server commits. The connection may close, or a selected result field may raise an error. Neither a timeout nor a top-level GraphQL error proves that no write occurred.

For the description update, retrying a committed change with revision 7 would encounter revision 8 and return a conflict. The client can reload the authorized book to inspect its state. A conflict alone does not prove whether this client's earlier attempt or another editor caused the change.

Do not automatically replace the expected revision and resubmit. That would bypass the protection against overwriting someone else's work. Let the editor reconcile the current value with the intended edit.

Operations such as creating an order need a separate, explicit idempotency contract if clients must retry safely. Neither an operation name nor an alias is an idempotency key. Returning fewer fields can reduce response work, but it cannot remove uncertainty about a lost response.

Summary

Queries select data without changing business state. Mutations invoke write capabilities and select their results. Use named operations, variables, aliases, fragments, and directives to express client needs clearly.

Distinguish request validation failures, execution errors, and typed business rejections. Serial mutation execution guarantees order within an operation, while authorization, atomic writes, concurrency protection, and retry safety remain responsibilities of the application.