What does a GraphQL introspection result report about a field's arguments and their defaults?
answer
- Arguments get their own record type
- Named, never positional
- The default arrives as text
- GraphQL source, not JSON
- One listing hides things by default
basics
~20 sEach field is a __Field record whose args are __InputValue records: name, description, type as a full wrapper chain, and defaultValue. The default is a String holding the value printed as GraphQL source, not a typed JSON value.
solid answer
~40 sA `__Field` carries `name`, `description`, `args`, `type` and the pair `isDeprecated` / `deprecationReason`. Every entry in `args` — and every field of an input object type — is an `__InputValue`: `name`, `description`, `type` (a `__Type`, wrapper chain and all) and `defaultValue`. The surprise is `defaultValue`'s type: it is `String`, holding the default **printed as GraphQL source**, so `limit: Int = 10` gives `"10"` and `sizes: [ImageSize!] = [THUMBNAIL, FULL]` gives the literal text `"[THUMBNAIL, FULL]"`, which is not JSON. It also separates two states people collapse: no default at all is `null`, while an explicit `= null` default is the string `"null"`. Deprecation is reported here too, but the listings filter it — `__Type.fields` and `__Type.enumValues` take `includeDeprecated: Boolean = false`, so deprecated entries are absent unless you ask.
code
json · 8 lines{
"name": "images",
"args": [
{ "name": "limit", "type": { "kind": "SCALAR", "name": "Int" }, "defaultValue": "10" },
{ "name": "sizes", "type": { "kind": "LIST", "name": null }, "defaultValue": "[THUMBNAIL, FULL]" },
{ "name": "after", "type": { "kind": "SCALAR", "name": "String" }, "defaultValue": null }
]
}go deeper
Know that introspection describes a field's arguments as well as its type: each argument has a name, a type and possibly a default, and arguments are always named rather than positional.
Explain the __Field and __InputValue record shapes, and be precise that defaultValue is a String of printed GraphQL source rather than a typed value — an enum or list default proves it, since neither is valid JSON.
Show that you would decide deliberately whether a pipeline reads the deprecated-inclusive listing, and that you know deprecation is documentation: the field still validates and still executes, and the response says nothing about it.
Own the policy question: which artefacts in the organisation are generated from the filtered listing and which from the complete one, and how a deprecation lands in downstream builds without breaking them at an unrelated point.
## The record behind a field When introspection describes a field, it returns a `__Field`: a `name`, an optional `description`, an `args` list of `__InputValue` records, the field's `type` (as a `__Type`, wrapper chain and all), and the deprecation pair `isDeprecated: Boolean!` / `deprecationReason: String`. Arguments are not a footnote in that record — they are the half that most consumers get wrong, because their representation has one genuine surprise in it. ## `__InputValue` Every argument on a field, and every field of an input object type, is described by the same record shape: * `name` — the argument name. GraphQL arguments are named, never positional, so this is the whole identity. * `description` — the block string attached to the definition, if any. * `type` — a `__Type`, so an argument's type carries the same `NON_NULL`/`LIST` wrapper chain a field's type does. `limit: Int = 10` is a bare `Int`; `ids: [ID!]!` is three records deep. * `defaultValue` — and here is the surprise. ## `defaultValue` is a string of GraphQL source `defaultValue` is declared as `String`, and it holds the default **printed as a GraphQL literal** — not a JSON value typed to match the argument. A default of `10` comes back as the string `"10"`. A default of `"THUMBNAIL"`… does not: an enum default prints unquoted, so the string is `"THUMBNAIL"` in JSON transport but its *contents* are the bare enum name, which is the same text you would write in a document. A list default prints as a list literal, an input object default prints as an object literal with unquoted keys. ```json { "name": "images", "args": [ { "name": "limit", "type": { "kind": "SCALAR", "name": "Int" }, "defaultValue": "10" }, { "name": "sizes", "type": { "kind": "LIST", "name": null }, "defaultValue": "[THUMBNAIL, FULL]" } ] } ``` A consumer that wants a typed value has to parse that text as GraphQL, using the argument's type to interpret it. Anyone who writes `JSON.parse` over it discovers the difference the first time an enum or an input object appears, because `[THUMBNAIL, FULL]` is not JSON. The second subtlety is the difference between **no default** and **a default of null**. An argument with no default has `defaultValue: null`. An argument explicitly declared `after: String = null` has `defaultValue: "null"` — the four-character string. Collapsing those two states loses a real distinction: one argument is absent unless supplied, the other is supplied as an explicit null. ## Deprecation flags and where they are hidden `__Field` carries `isDeprecated` and `deprecationReason`, populated from `@deprecated`. But the listing that returns those records filters by default: `__Type.fields` takes `includeDeprecated: Boolean = false`, and so does `__Type.enumValues`. Ask for a type's fields without the argument and the deprecated ones are simply not in the list — no flag, no marker, absent. That default exists so that surfaces built to *steer* callers (completion lists, generated documentation) point at the live contract without extra work. It is worth being precise about what deprecation does and does not do: it is documentation, not removal. A deprecated field still validates and still executes; the response carries no warning. The only place the deprecation exists at runtime is in introspection, and only if you ask for it. Extending the same treatment to **arguments and input object fields** — deprecating them and filtering them with the same `includeDeprecated` pattern — came later than field and enum-value deprecation, so a consumer that must work against older servers should not assume it is there. ## Where this bites Any pipeline that produces an artefact from introspection has to decide, deliberately, which listing it wants. A generator that must keep compiling against a schema mid-migration wants `includeDeprecated: true`, or a field vanishes from the generated shape on the day someone adds an annotation, and the failure surfaces as a compile error a long way from its cause. A documentation surface aimed at new integrators wants the default. Neither choice is wrong; failing to make it is. ```graphql query FieldDetail { __type(name: "Artwork") { fields(includeDeprecated: true) { name isDeprecated deprecationReason args { name defaultValue type { kind name ofType { kind name } } } } } } ``` ## Watch for * Parsing `defaultValue` as JSON. * Treating `defaultValue: null` and `defaultValue: "null"` as the same thing. * Assuming a type's `fields` list is complete without passing `includeDeprecated`. * Believing a deprecated field stops resolving, or that the response says anything about it. * Forgetting that an argument's `type` is a wrapper chain like any other type.
- How do you tell an argument with no default from one whose default is null?By the two distinct values of `defaultValue`. No default at all gives JSON `null`; an argument declared `after: String = null` gives the four-character string `"null"`, because the default is printed as GraphQL source. Collapsing them loses a real difference: one argument is simply absent unless supplied, the other is supplied as an explicit null.
- Why does a type's fields listing hide deprecated fields unless you ask for them?`__Type.fields` and `__Type.enumValues` take `includeDeprecated: Boolean = false`, so surfaces meant to steer callers show the live contract by default. Anything that must stay complete across a migration — a generated artefact, an audit — has to pass `true` explicitly, or a member disappears the day someone annotates it.
saying these in an interview costs you the question
- Parses defaultValue as JSON
- Treats no default and a null default as the same
- Assumes the fields listing is complete by default
- Thinks a deprecated field stops resolving