skip to content

What do GraphQL's __schema and __type(name:) meta-fields return, and where may they appear?

level: middleimportance: must knowfreq 52%

answer

  1. The server describes itself in its own language
  2. One whole type system, one single lookup
  3. Roots, types and directive definitions
  4. Query operation root only, never at depth
  5. Unknown type name returns null

basics

~20 s

__schema returns the whole type system: every type, the query, mutation and subscription root types, and the directive definitions. __type(name:) returns one named type, or null when the schema has none. Both are legal only at a query operation's root.

solid answer

~40 s

Both are meta-fields on the **query root type**, so a caller learns the contract by sending an ordinary document over the ordinary transport — there is no side channel. `__schema` returns a `__Schema` record: `types` (every type in the system), `queryType` (non-null), `mutationType` and `subscriptionType` (nullable, since a schema need not have them), and `directives` (the directive **definitions**, with their arguments, locations and repeatability). `__type(name: String!)` is the targeted lookup and returns a **nullable** `__Type`, so an unknown name yields `null` rather than an error. The specification restricts both to the root of a **query** operation — not a mutation root, not a subscription root, and not at depth. Like `__typename`, they are meta-fields, so they never appear in the query root type's own `fields` list.

code

graphql · 15 lines
graphql
query SchemaProbe {
  __schema {
    queryType { name }
    mutationType { name }
    subscriptionType { name }
    types { name kind }
    directives { name locations isRepeatable }
  }
  __type(name: "Artwork") {
    kind
    description
    fields { name }
    interfaces { name }
  }
}

go deeper

for a junior

Know that a GraphQL server can describe itself, that you ask with a normal query, and that __schema gives the type list and root types while __type looks up one type by name.

for a middle

Explain the __Schema record field by field, why mutationType and subscriptionType are nullable, and the rule that both meta-fields are legal only at the root of a query operation. Expect to be asked what a missing __type name returns.

for a senior

Be ready to say what an introspection result faithfully preserves and what it drops — descriptions survive, comments, ordering and file layout do not — and why schema comparison must run over a canonical printed form rather than raw files.

for a principal

Frame introspection as the contract-distribution mechanism for the organisation: who is entitled to read a running server's type system, whether the checked-in artefact or the live endpoint is authoritative, and what that choice costs consumers.

## The schema, asked for in the same language as the data A GraphQL server describes its own type system through two meta-fields on the **query root type**: `__schema` and `__type(name:)`. There is no side channel, no well-known path and no separate protocol — a caller sends an ordinary document over the ordinary transport and gets an ordinary response back. That single design decision is the reason the ecosystem around GraphQL exists at all: anything that can send a query can learn the whole contract. ## `__schema` `__schema` returns a single `__Schema` record whose fields are the schema itself, decomposed: * `types: [__Type!]!` — every type in the type system. * `queryType: __Type!` — the object type serving as the query root. Non-null, because a schema must have one. * `mutationType: __Type` — nullable; a schema need not support mutations. * `subscriptionType: __Type` — nullable, for the same reason. * `directives: [__Directive!]!` — the directive **definitions** the schema declares, each with its name, its arguments, the locations it may be applied at, and whether it is repeatable. Note what `types` contains. It is not only the types you wrote. A museum collection schema whose SDL file declared 49 type definitions came back with 62 entries: the 49 authored types, the five built-in scalars (`Int`, `Float`, `String`, `Boolean`, `ID`), and the eight introspection meta-types themselves — `__Schema`, `__Type`, `__Field`, `__InputValue`, `__EnumValue`, `__Directive` and the two enums `__TypeKind` and `__DirectiveLocation`. Introspection describes introspection; that is not a bug, and a consumer that renders `types` unfiltered will show the meta-types to its users. ## `__type(name:)` `__type` takes a required `name: String!` and returns a nullable `__Type`. It is the targeted probe: when you already know which type you care about, you ask for that one instead of pulling the whole type system. Because the return type is nullable, a name the schema does not define yields `null` in `data` — **not** an error, and not a fuzzy match. Code that treats a missing type as an exception will be surprised by a quiet null. ```graphql query SchemaProbe { __schema { queryType { name } types { name kind } directives { name locations isRepeatable } } __type(name: "Artwork") { kind description fields { name } interfaces { name } } } ``` ## Where they may appear The specification allows `__schema` and `__type` **only at the root of a query operation**. They are not selectable at the root of a mutation or a subscription, and they are not selectable at depth — there is no `artwork { __schema { ... } }`. This is easy to forget because `__typename`, the third meta-field, has the opposite rule: it is available almost everywhere and forbidden only at a subscription root. Like `__typename`, these two are meta-fields rather than schema fields, so they never appear in the query root type's own `fields` list. A tool that reconstructs SDL from an introspection result and then re-introspects the reconstruction gets the same answer, because nothing about them lives in the type system. ## What comes back is a record graph, not a document An introspection result is structured data: types, fields, arguments, enum values, descriptions. Descriptions survive, because a description is part of the type system — it is the block string attached to a definition. What does **not** survive is anything that is only text: `#` comments, blank lines, the order in which definitions were written, and the file boundaries the author used. An SDL printer that reads an introspection result produces a canonical rendering of the same type system, not a byte-for-byte copy of the author's file. Teams that diff schemas learn this quickly: compare the printed canonical form on both sides, never the raw files. ## What it is for, in an interview answer The honest framing is that `__schema` turns a running server into the authoritative description of its own contract. Every tool that offers field completion, every generator that produces typed client code, every checker that compares a deployed schema to the one clients were built against is ultimately a consumer of these two meta-fields. That is also why the *availability* of introspection on a public endpoint is an operational decision with consequences beyond convenience — a topic in its own right. ## Watch for * Claiming the server exposes its SDL text at some conventional URL. Nothing in the specification says so. * Selecting `__schema` inside a mutation, or at depth. * Treating an unknown `__type` name as an error rather than a null. * Confusing `__type(name:)`, a root lookup, with `__typename`, a per-value discriminator.

  • What does __type return for a name the schema does not define?
    Null. Its return type is a nullable `__Type`, so a miss is an ordinary null in `data` — not a request error, not a field error, and certainly not a fuzzy match. Consumers that treat an absent type as an exception need to check for the null explicitly.
  • Does the __schema types list contain more than the types the author wrote?
    Yes. It also carries the five built-in scalars and the eight introspection meta-types — `__Schema`, `__Type`, `__Field`, `__InputValue`, `__EnumValue`, `__Directive`, `__TypeKind` and `__DirectiveLocation`. A museum schema declaring 49 types reports 62 entries. Anything rendering that list to humans usually filters the double-underscore names.
  • Where does an introspection result list the schema's directives, and what does each entry carry?
    Under `__schema { directives }`, as `__Directive` records: `name`, `description`, `args` as `__InputValue` records, `locations` from the `__DirectiveLocation` enum, and `isRepeatable`. These are the **definitions** the schema declares, which is a different thing from where those directives have been applied.

saying these in an interview costs you the question

  • Claims the server serves its SDL text at a conventional URL
  • Thinks __schema is a normal field you implement
  • Selects __schema at depth or on a mutation root
  • Says an unknown __type name raises an error
  • Confuses __type(name:) with the __typename meta-field

context