skip to content

Operation Types & Names

The three operation types, the keyword-less query shorthand, and why a document holding several operations needs an operationName. Interviewers use it to see if you have really written requests.

part ofGraphQLoverview, primer and where to startread it →
on this pageshow

questions

4

What are GraphQL's three operation types, and what distinguishes them?

level: juniorimportance: must knowfreq 84%

answer

  1. Three keywords, three intents
  2. Each reaches the schema by a root
  3. Only one root type is mandatory
  4. Read, write-then-read, long-lived stream
  5. Read-only is assumed, not enforced

basics

~20 s

Query, mutation and subscription. Each enters the schema through its own root type and carries a different intent: a read, a write followed by a read of the result, and a long-lived stream of events. Only the query root is required.

solid answer

~50 s

GraphQL defines exactly three operation types: `query`, `mutation` and `subscription`. Each enters the schema through its own root operation type, and only the query root is mandatory — a schema that declares no mutation root simply cannot accept mutation operations at all. What separates them is intent plus one execution rule: a query reads, a mutation writes and then reads back the result of that write, and a subscription opens a long-lived stream that produces one result per event. A mutation's top-level fields are executed in order rather than concurrently. Crucially, nothing *enforces* a read-only query: a resolver behind a query field can write to a database and no server will stop it. Side-effect-free queries are an expectation the specification states and leans on — it is why a server may resolve a query's root fields concurrently — not a rule the runtime checks.

code

graphql · 20 lines
graphql
query ClinicSchedule($clinicianId: ID!) {
  clinician(id: $clinicianId) {
    fullName
    appointmentsToday { id startsAt }
  }
}

mutation BookSlot($slotId: ID!, $patientId: ID!) {
  bookAppointment(slotId: $slotId, patientId: $patientId) {
    id
    startsAt
  }
}

subscription SlotReleased($clinicId: ID!) {
  slotReleased(clinicId: $clinicId) {
    id
    startsAt
  }
}

go deeper

for a junior

Be ready to name all three without hesitating and say in one sentence what each is for. This is a warm-up question, and fumbling the third one colours everything the interviewer asks afterwards.

for a middle

Explain that each operation type reaches the schema through its own root object type, that only the query root is required, and that the read/write split is intent the server assumes rather than a rule it checks.

for a senior

Show the consequences that hang off the operation type: whether a response may be cached, whether root fields may be resolved concurrently, and whether the request needs a long-lived connection at all.

for a principal

Own the policy: whether writes behind query fields are ever permitted in your schemas, how the operation type drives caching and rate-limit tiers, and what it costs the organization when a team classifies a field wrongly.

## Three, and the schema roots behind them GraphQL's grammar admits exactly three operation types, spelled `query`, `mutation` and `subscription`. They are not three flavours of one thing; each is a distinct entry point into the schema, and the schema decides which object type sits at each entry point. Written out explicitly that is a schema definition: ```graphql schema { query: Query mutation: Mutation subscription: Subscription } ``` A schema that omits this block gets the conventional defaults — the object types literally named `Query`, `Mutation` and `Subscription`, if they exist. Those names are a default, not a requirement: binding `query: HospitalReads` is legal, and rare enough that seeing it should make you look twice. Only the query root is mandatory. A schema for a hospital appointment graph that exposes reads and nothing else declares a `Query` root and stops there; a client that sends a `mutation` against it fails before execution, because the operation names an operation type the schema does not define and there is no root type to check the selection set against. Mutation and subscription roots are optional, and their absence is a hard boundary rather than an error discovered halfway through a request. ## What actually differs between them Three things separate the operation types, and it pays to keep them apart. **Intent.** A query reads. A mutation writes and then reads back the result of that write — the selection set hanging off a mutation field is how you get the new state without a second round trip. A subscription registers interest in a source of events, and each event produces one result shaped by the same selection set. **Root-field execution.** The specification treats a query's top-level fields as independent and permits a server to resolve them concurrently. A mutation's top-level fields are executed in order instead. The mechanics of that ordering are the execution algorithm's business; what matters here is that the operation type, and nothing else in the document, is what selects the behaviour. **Lifecycle.** A query and a mutation are one document in, one result out, done. A subscription is long-lived: the server holds an event source and emits a result per event until one side ends it. That difference drives how a subscription is carried and operated, which is a separate subject from what a subscription *is*. ## Read-only is a convention, not an enforcement This is the part candidates get wrong, and it is worth being blunt. Nothing in a GraphQL server stops a resolver behind a query field from writing to a database. There is no marker on a field declaring that it mutates, no validation rule that inspects resolver bodies, and no runtime that rolls anything back. The specification *describes* a query as a read and expects query fields to be side-effect-free — and it leans on that expectation when it permits a server to resolve a query's fields concurrently. Break it and you have not tripped a rule the machine checks; you have falsified an assumption that several layers above you are already making. Those layers are real. A response cache keys on the operation type before it looks at anything else. Edge caching will serve a stored result for a query and never for a mutation. Rate limiting is commonly tiered by operation type. Concurrent field resolution assumes independence. Smuggling a write behind a query field is how you end up with a write that silently never happens, because your request was answered from a cache that was entitled to answer it. ## What the operation type is not It is not a transport choice and it is not an HTTP verb. The operation type is a property of the document; the same operation type travels over quite different transports, and choosing a transport does not choose an operation type for you. It is also not a permission model. `mutation` does not mean authenticated and `query` does not mean public. Authorization is orthogonal, and a schema whose read side is more sensitive than its write side is entirely ordinary — a patient's full appointment history is more dangerous to leak than the ability to request a booking that a human then approves. ## What the question is really testing A weak answer names two types and gropes for the third. A competent answer names three and says what each is for in a sentence. A strong answer adds the two facts that carry consequences: only the query root is required of a schema, and the read/write distinction is intent the server assumes rather than enforces. If you can then say *why* a server cares — because concurrency and caching both hang off that assumption — you have said everything the question can hold.

  • Does anything stop a resolver behind a query field from writing to the database?
    No. The specification describes a query as a read and expects query fields to be side-effect-free, but there is no marker, no validation rule and no runtime check. A server may resolve a query's root fields concurrently precisely because it assumes independence. Hide a write behind a query field and you also make the response eligible for caching layers that key on the operation type, so the write can silently never run.
  • What happens when a client sends a mutation to a schema that declares no mutation root type?
    The request fails before execution. The operation names an operation type the schema does not define, so there is no root type to check the selection set against; the response carries an `errors` entry and no `data` key. Only the query root operation type is required of a schema — mutation and subscription roots are optional, and omitting one is a hard boundary rather than a runtime surprise.
  • Must the root operation types always be the object types named Query, Mutation and Subscription?
    No, those are only the defaults a schema falls back to when it omits an explicit schema definition. A schema definition may bind any object type to any root — `schema { query: HospitalReads, mutation: HospitalWrites }` is legal. Almost every codebase uses the default names, so treat non-default roots as valid but unusual, and check the schema definition rather than assuming.

The operation type is the label on the parcel: it tells the server whether you are asking for a copy of something, asking to change it, or asking to be told every time it changes.

saying these in an interview costs you the question

  • Names only queries and mutations, forgets subscriptions
  • Claims the server rejects a write inside a query resolver
  • Says the operation type is really an HTTP verb
  • Believes every schema must declare all three root types
  • Describes a subscription as polling with a different keyword
  • Treats mutation as meaning authenticated and query as public

context

open as a page

How does a GraphQL server choose which operation to run in a multi-operation document?

level: middleimportance: must knowfreq 61%

basics

~10 s

By the operationName the caller supplies. With exactly one operation in the document the name may be omitted; with two or more, a missing or unmatched operationName is a request error and nothing executes.

open as a page

When is GraphQL's query shorthand, a bare selection set with no keyword, legal?

level: middleimportance: should knowfreq 54%

basics

~20 s

Only when the document holds exactly one operation, that operation is a query, and it declares no variables and carries no directives on the operation itself. Then both the query keyword and the operation name may be omitted.

open as a page

Why can't one GraphQL subscription operation watch two event streams at once?

level: seniorimportance: nice to knowfreq 24%

basics

~20 s

Because a subscription operation's root selection set must collapse to exactly one field, and that field is what creates the event source. Two streams means two subscription operations, or one root field emitting a union of event shapes.

open as a page