What is a variable in a GraphQL operation, and which types may it be declared as?
answer
- Values travel beside the document
- Declared on the operation, not the field
- Only input types are allowed
- $name colon Type, optional constant default
- An object type is never a variable type
basics
~20 sA GraphQL variable is a named, typed placeholder declared on the operation and filled from a separate values map sent with the request. Only input types are allowed: scalars, enums, input objects, and their list or non-null wrappers.
solid answer
~50 sA variable is a typed placeholder declared in the operation definition - `query PlaylistTracks($id: ID!, $first: Int = 25)` - and referenced as `$name` anywhere an argument value is expected. Its value is never written into the document; it travels beside it in a separate map keyed by variable name, so one document text serves every call. A variable's declared type must be an **input type**: a scalar, an enum, an input object, or any list and non-null wrapping of those. Output types - object, interface and union types - are rejected when the document is validated, because a caller supplies values, not selections. A variable may carry a default written as a constant literal after `=`, and that default cannot reference another variable. Names are unique within an operation, every variable used must be declared, and every declared variable must be used.
code
graphql · 10 linesquery PlaylistTracks($id: ID!, $first: Int = 25, $markets: [Market!]) {
playlist(id: $id) {
name
tracks(first: $first, markets: $markets) {
id
title
durationMs
}
}
}go deeper
Be ready to write an operation with variables from memory: the parenthesised declarations after the operation name, $name at the use site, and the values map keyed without the dollar sign. Knowing that only input types are allowed is the second half of the answer.
Explain why the input-type restriction exists rather than reciting it, and cover defaults precisely: constant literals only, legal on a non-null variable, and never a reference to another variable. Mention the declared-and-used rules that validation enforces.
Show that you treat the document as a stable, reviewable artefact and the values map as the only moving part. Be ready to explain how fragments inherit variable requirements from the operations that spread them, and why a shape change needs a new document, not a new variable.
Own the consequence for a shared graph: a fixed set of documents with parameterised values is what makes operations reviewable, testable and attributable to a team, whereas client-assembled query text turns every deploy into an unbounded surface nobody has seen.
## The split a variable creates A GraphQL request carries two things: a **document**, which is the text describing what to fetch, and a **variable values map**, which supplies the parameters. A variable is the joint between them. Declaring one tells the server "this operation takes a parameter of this type"; referencing `$name` inside the operation says "put its value here". The values themselves arrive as an ordinary keyed map alongside the document, most often JSON. That split matters because the document is *structure* and the values are *data*. A music catalogue client that shows one playlist screen has exactly one document, whatever playlist the user opened. The document is written once, checked once against the schema, and reused; only the map changes between calls. ## The grammar Variables are declared on the **operation**, in parentheses after the operation name, and nowhere else - not on a field, not on a fragment definition: ```graphql query PlaylistTracks($id: ID!, $first: Int = 25, $markets: [Market!]) { playlist(id: $id) { name tracks(first: $first, markets: $markets) { id title } } } ``` Each entry is `$name: Type` with an optional `= default`. The `$` is part of the reference syntax, not part of the name in the values map: the map key here is `"id"`, not `"$id"`. A variable can be used as a whole argument value, as a field of an input object literal, or as an item inside a list literal - anywhere the grammar admits an input value. ## Only input types The specification restricts a variable's declared type to an **input type**, and the rule is enforced during validation: - built-in and custom **scalars** (`Int`, `Float`, `String`, `Boolean`, `ID`, and anything the schema declares with `scalar`), - **enums**, - **input object types**, - and any **list** (`[T]`) or **non-null** (`T!`) wrapping of those. Declaring `$track: Track` where `Track` is an object type is invalid, and so is a variable typed as an interface or a union. The reason is direct: a caller sends *values*, and object, interface and union types describe things a server *returns*, whose shape depends on the selection set the document asked for. There is no way to write one down as an argument. This is also why a schema tends to grow a parallel family of input types - a `TrackFilterInput` beside a `Track` - rather than reusing output types for arguments. ## Defaults A variable definition may carry a default value: ```graphql query Chart($first: Int = 25, $market: Market! = GB) ``` Two details are worth holding on to. First, the default must be a **constant literal** - a number, a string, an enum name, a list or input object built from constants. It cannot reference another variable, because there is no ordering in which variables are resolved against each other. Second, a **non-null** variable may still carry a default. `$market: Market! = GB` is perfectly legal: the caller may omit `market` and get `GB`, but may not send `null` for it, because null is not a value of a non-null type. ## The document-level rules around variables Beyond the type restriction, a handful of validation rules govern variables in a document. Variable names are **unique within an operation**. Every variable a document *uses* must be **declared** by the operation that runs it, and every variable a document *declares* must be **used** - an unused declaration is an error, not dead weight the server tolerates. Because a named fragment cannot declare variables of its own, a fragment that references `$first` inherits the requirement: every operation that spreads that fragment, directly or transitively, must declare `$first`. There is also a compatibility rule between a variable's declared type and the argument position it lands in. A variable typed `Int!` may be used where `Int` is expected; a variable typed `Int` may **not** normally be used where `Int!` is expected, since a nullable variable could arrive as null in a position that forbids it. That is relaxed when a default rescues it - either the variable definition has a non-null default, or the argument itself declares one - because then no request can leave the position empty. ## What a variable is not A variable is not text substitution. The server does not paste the value into the document and re-parse it; the parsed document holds a *reference* to a variable, and the value is resolved separately when the request is executed. A consequence a beginner is often surprised by: a variable cannot stand in for a **field name**, an **alias**, a **type condition**, or a **directive name**. `$fieldName { ... }` is not a thing. Variables parameterise values only; if the client needs a different shape, it needs a different document, or an executable directive to include and skip parts of the one it has. A final practical note: variables never appear in the response. The response echoes the document's response keys and the data behind them, and knows nothing about how the arguments were supplied.
- Can a variable be declared with an object type such as `Track`?No. A variable's declared type must be an input type - a scalar, an enum, an input object, or a list or non-null wrapping of those - and the document fails validation otherwise. Object, interface and union types describe what a server returns, and their shape depends on the selection set, so there is no way to write one down as a supplied value. Schemas therefore grow separate input types, such as a `TrackFilterInput` beside the `Track` object type.
- May a nullable variable be used where a non-null argument is expected?Only when a default guarantees the position is never empty. The general rule is that a variable typed `Int` cannot be used where `Int!` is expected, because the caller could send null into a position that forbids it. The specification relaxes this when the variable definition carries a non-null default, or when the argument itself declares a default: in both cases an absent or defaulted value still yields something legal, so the usage is allowed.
- Can a variable stand in for a field name or a type condition?No. Variables parameterise input *values* only - argument values, input object fields, list items. Field names, aliases, fragment type conditions and directive names are all part of the document's structure, fixed at parse time. If a client needs a different shape it sends a different document, or uses `@skip` and `@include` with a boolean variable to include or omit parts of the one it has.
Declaring variables turns a hardcoded request into a function with parameters: the body is written once, and each call passes a different argument list.
saying these in an interview costs you the question
- Thinks the server interpolates variable values into the document text
- Declares a variable with an output object or union type
- Believes variables are declared on the field, not the operation
- Assumes an undeclared $name is still filled from the values map
- Thinks a variable default may reference another variable
- Expects a variable to substitute a field name or type condition