Introspection

This section documents the current GraphQL specification (October 2021), plus the working draft for @defer/@stream/@oneOf, and the GraphQL-over-HTTP specification, as published at graphql.org and spec.graphql.org — which are the references these pages are written and verified against — and, for the integration pages, against Spring for GraphQL (2.0.x), Strawberry, Ariadne, and the client docs for Apollo Client / urql / Relay. No specific patch version is pinned. Some surfaces (Apollo Router/managed federation, graphql-ws internals, the Relay compiler internals, GraalVM native) are linked rather than documented in depth.

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production, as GraphQL tooling iterates quickly.

GraphQL schemas are queryable through the schema itself: a small set of reserved meta-fields lets any client ask a server what types, fields, and operations it exposes, using the same query syntax as an ordinary request. This page covers those meta-fields, the tooling built on top of them, and why introspection is often the first thing disabled when a schema moves toward production.

The introspection meta-fields

Every GraphQL schema implicitly defines three meta-fields, prefixed with a double underscore to keep them out of the way of application-defined fields, which the spec reserves entirely for this purpose:

Meta-field What it returns

__schema

The whole schema, queryable from the root of a query: every type, the query/mutation/subscription root types, and any declared directives.

__type

A single named type looked up by name, with its kind, fields, possible values (for an enum), or possible types (for a union or interface).

__typename

The concrete type name of whatever object is being resolved at that point in the result, available on any selection set, not only at the schema root.

schema and type describe the schema itself and are queried at the root of an operation, alongside (or instead of) ordinary fields. __typename is different in kind: it is available inside any selection set in an ordinary query, and it answers "what type did I actually get back", which matters most when a field’s declared return type is an interface or a union and the client needs to know which concrete type came back before it can select type-specific fields.

{
  __schema {
    types {
      name
      kind
    }
  }
}

Looking up one named type in isolation avoids downloading the entire schema when only a handful of fields are of interest:

{
  __type(name: "Book") {
    name
    kind
    fields {
      name
      type {
        name
        kind
      }
    }
  }
}

__typename shows up constantly once a query touches a union or interface field, since the response otherwise gives no direct signal of which concrete type was returned:

{
  search(term: "darkness") {
    __typename
    ... on Book {
      title
    }
    ... on Author {
      name
    }
  }
}

The full grammar for these fields — including the directives, queryType, mutationType, and subscriptionType fields on schema, and the complete shape of the Type and __Field introspection types — is defined in the specification’s Introspection guide, which is the reference this section assumes. Schema and Type System covers the type system that introspection describes (object types, interfaces, unions, enums, scalars) in depth; this page only covers how that same information is queried back out at runtime.

What introspection makes possible

Because introspection exposes the full schema through the same protocol as any other query, tooling can work against any GraphQL server generically, without hand-written integration for each API:

  • In-browser IDEs. GraphiQL, Apollo Sandbox, and GraphQL Playground (introduced in Getting Started with GraphQL) all send an introspection query on load and use the result to drive autocomplete, inline field documentation, and query validation as a query is typed — none of that would be possible without a machine-readable description of the schema to query against.

  • Client-side type generation. Tools such as GraphQL Code Generator run an introspection query (or read a schema file produced from one) and emit typed client code from it — TypeScript types for a query’s result shape, typed React hooks, or typed operation builders — so a client’s code stays in sync with the server’s schema instead of hand-maintaining parallel type definitions.

  • Schema diffing and documentation. CI tooling that checks a schema for breaking changes between releases, or that publishes rendered schema documentation, typically works from an introspection result (or the equivalent Schema Definition Language dump) rather than parsing server source code.

  • API exploration. Pointing any of the tools above at an unfamiliar public endpoint and browsing its schema is the fastest way to understand what an API offers before writing a single query by hand, exactly because introspection makes the schema self-describing.

In every case, the tool is not specific to one server’s implementation — it only depends on the server answering schema and type queries the way the specification requires.

Why production servers restrict it

The same completeness that makes introspection useful for tooling also hands an attacker a full map of the API’s surface: every type, every field, every argument, and every deprecated field still lingering in the schema, all without needing to guess field names or read any documentation. Related to this, many GraphQL servers also emit a "did you mean x?" suggestion when a query references a field that almost matches a real one — a small convenience for developers that, left enabled, lets an attacker enumerate a schema’s field names one guess at a time even with introspection itself turned off.

Disabling introspection (and these field-suggestion hints) outside of development is a common production hardening step for exactly that reason. This page only flags the concern; for how and when to actually turn introspection off, how that trade-off interacts with API consumers who rely on it, and the other demand-control measures a production schema typically pairs it with, see Security and Demand Control.

Introspection as a debugging tool

Introspection is also useful directly, independent of any client tooling built on it — pointing a raw introspection query at a server (in an environment where it is still enabled) is often the fastest way to confirm what a schema actually declares, rather than trusting documentation that may have drifted from the running server:

{
  __type(name: "Mutation") {
    fields {
      name
      args {
        name
        type {
          name
        }
      }
    }
  }
}

Run against a live endpoint, that query answers "what mutations exist and what do they take" directly from the source of truth — the schema the server is actually executing against — with no dependency on separately maintained documentation staying current.