skip to content

What does a GraphQL introspection result report about a field's arguments and their defaults?

level: middleimportance: nice to knowfreq 22%

answer

  1. Arguments get their own record type
  2. Named, never positional
  3. The default arrives as text
  4. GraphQL source, not JSON
  5. One listing hides things by default

basics

~20 s

Each 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 s

A `__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
json
{
  "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

for a junior

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.

for a middle

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.

for a senior

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.

for a principal

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

context