skip to content

What does a codegen tool lose when it reads a schema by introspection instead of SDL?

level: seniorimportance: nice to knowfreq 26%

answer

  1. Two possible inputs, not equally complete
  2. Definitions survive; something else does not
  3. Deprecation got its own field for a reason
  4. Applied directives have no general channel
  5. A build input needs a version on it

basics

~20 s

Directive applications. An introspection result describes types, fields, arguments and directive definitions, but has no general channel for which directives are applied where, so custom directives on fields and types are invisible unless the tool reads SDL text.

solid answer

~50 s

An introspection result is a description of the executable schema a server built: every type, field, argument, enum value and description, plus the *definitions* of the directives the schema declares. What it does not carry is which directives are **applied** to which type or field — there is no general applied-directive channel in the introspection system. The specification instead surfaces a few built-in cases as dedicated fields, notably deprecation state and a custom scalar's specification URL, which is itself evidence that applied directives are otherwise unavailable. So a generator pointed at an endpoint cannot see a custom directive marking a field's scalar mapping, its ownership or its auth requirement, while the same generator reading the SDL file can. Type extensions are also already merged and comments are gone. The practical rule is to feed generation a published SDL artefact pinned to a known schema version, not whichever environment happens to answer.

code

graphql · 8 lines
graphql
directive @sensitive(reason: String!) on FIELD_DEFINITION

type Patient {
  id: ID!
  displayName: String!
  nhsNumber: String! @sensitive(reason: "identifier")
  dateOfBirth: Date @deprecated(reason: "Use birthYear")
}

go deeper

for a junior

Know that a generator can be fed either an SDL file or the result of introspecting a running server, and that the two are not identical inputs. Nobody expects the details at this level.

for a middle

Be able to say what an introspection result contains — types, fields, arguments, descriptions, directive definitions — and name the notable gap: which directives are actually applied where. That is the difference that decides which input a tool needs.

for a senior

Argue the operational case: a pinned, published SDL artefact makes generation reproducible and makes a schema change a visible dependency bump, while an endpoint URL leaves nobody able to say which schema version a release was built against.

for a principal

Own the contract between server and client builds. Decide who publishes the schema artefact, how it is versioned, and whether client teams pin it — that choice determines whether a schema change is a tracked event or something clients discover in production.

## Two ways to hand a generator a schema A typed client generator needs to know the type system. It can get it from an **SDL file** — the schema definition language text, committed or published as a build artefact — or from an **introspection result**, the JSON a server returns when the introspection meta-fields are queried against a live endpoint. Pointing at a URL is the more convenient of the two and is why so many setups start there. They are not equivalent inputs. ## What survives introspection Everything a client strictly needs to type a response survives: object, interface, union, enum, input and scalar types; every field with its arguments; every nullability and list modifier; default values; descriptions; and the list of directive **definitions** the schema declares, with their names, arguments and valid locations. ## What does not **Directive applications.** Knowing that a schema *defines* `@sensitive` tells a generator nothing about which fields carry it. The introspection system has no general way to ask "which directives are on this field", and the specification's own workarounds prove the point: deprecation is exposed through dedicated fields on fields and enum values rather than as an applied `@deprecated`, and a custom scalar's specification URL is exposed through its own field rather than as an applied `@specifiedBy`. Each built-in that clients needed to see got a purpose-built channel, because there was no generic one. That matters the moment your generation is configured by directives. A schema that marks which fields a mapped custom scalar covers, or which fields require a permission, or which are internal-only, is handing the generator instructions it will simply not receive over introspection. The generated client compiles, is wrong in a way no compiler will catch, and the omission is silent — there is no error, only an absence. **Everything textual.** Comments are not descriptions and do not survive. `extend type` blocks are already merged into the built schema, so the introspected picture cannot tell you which part of a type came from where. Field ordering and formatting are the server's, not your file's. ## The operational half, which is usually the real reason Even setting content aside, an endpoint is a poor input for a build step. Introspection is frequently disabled on production endpoints, so a generator pointed there gets nothing. Pointed at a staging endpoint instead, it gets *something* — whatever schema version happens to be deployed at the moment the build ran. Two builds an hour apart can generate against different schemas with no record of which. For a four-person platform team maintaining several client applications, that is a supply chain with no version in it: nobody can answer "which schema did this release's client types come from". The fix is the same one every other build input gets. Publish the SDL as a versioned artefact from the server's own build — printed from the schema the server actually runs, so it cannot drift from reality — and have client builds consume that pinned artefact. Generation becomes reproducible, a schema change becomes a visible dependency bump in the client repository, and the diff of the artefact is a readable record of what changed. ## When introspection is still the right input It is not always wrong. Exploring an API you do not own, or generating against a third-party endpoint whose SDL is not published, leaves introspection as the only door. A one-off local generation against a dev server is fine. The distinction is between a **development convenience** and a **build input**: the first can be a URL, the second should be a file with a version on it. ## The question behind the question Interviewers who ask this are usually probing whether you know that an introspection result is a *lossy projection* of the SDL rather than a serialization of it. Candidates who have only ever pointed a tool at `http://localhost/graphql` assume round-tripping SDL through introspection returns the same text. It does not, and the missing directive applications are exactly the part a sophisticated codegen configuration depends on.

  • If applied directives are not introspectable, why does deprecation survive a round trip?
    Because deprecation is not carried as an applied directive at all. The introspection system exposes it as dedicated state on a field or enum value — a flag plus a reason — so tools can render and lint it. A custom scalar's specification URL is handled the same way. Both are special cases added because clients needed them, not evidence of a general mechanism.
  • How do you keep the SDL file a generator reads from drifting from the schema the server runs?
    Print it from the server's own build rather than hand-maintaining a copy, publish it as a versioned artefact, and have client builds consume that artefact by version. Then the file cannot describe a schema nobody is running, updating it is a visible dependency bump in the client repository, and the artefact's diff is the record of what changed between releases.
  • When is pointing a generator at a live endpoint still the right call?
    When you do not own the schema and no SDL is published — a third-party API, or exploring an unfamiliar service — and for one-off local generation against a dev server. The line is between a development convenience and a reproducible build input: the first can be a URL, the second needs a pinned file so two builds cannot silently generate against different schema versions.

saying these in an interview costs you the question

  • Says introspection returns the SDL text
  • Thinks applied directives are introspectable
  • Assumes a directive definition implies its applications
  • Points a production build at a live endpoint
  • Cannot say which schema version a client was built from
  • Believes comments survive as descriptions

context