What does GraphQL's @specifiedBy directive attach to a custom scalar, and who reads it?
answer
- A custom scalar's name says nothing
- The schema points somewhere instead of describing
- One argument, one location
- Nothing fetches it, nothing enforces it
- Introspection surfaces it for tooling
basics
~20 sIt attaches a URL to a custom scalar definition, pointing at a human-readable specification of how that scalar's values are serialized. The server never fetches or enforces it — humans and tooling read it through introspection.
solid answer
~50 s`@specifiedBy(url: String!)` is a built-in type-system directive whose only valid location is a **scalar type definition**. Its argument is a URL naming a human-readable specification for the scalar's value format — for a date-time scalar, the document describing that timestamp format. It has **no execution semantics**: the server does not fetch the URL, does not validate values against it, and coercion behaves identically whether the directive is present or not. Its readers are downstream. Introspection surfaces it on the scalar's type entry, so a schema browser can link it, a reviewer reading the schema can find out what the format actually is, and a client generator can in principle recognise a known URL and map that scalar onto a real client-side type instead of an opaque one. It is a documentation channel that happens to be machine-readable — which matters precisely because a custom scalar otherwise tells clients nothing but its name.
code
graphql · 8 linesscalar DateTime
@specifiedBy(url: "https://datatracker.ietf.org/doc/html/rfc3339")
type Trip {
id: ID!
startedAt: DateTime!
endedAt: DateTime
}go deeper
Recall the shape: one URL argument, applied to a custom scalar, pointing at a human-readable format specification. Nothing more is expected of you here.
Be able to say what it does not do — no fetching, no validation, no effect on coercion — and note that introspection exposes the URL so tooling can use it.
Frame it as the only machine-readable channel a custom scalar has beyond its name, and connect that to why unfamiliar custom scalars land as opaque values in generated clients.
If your organisation standardises custom scalars across services, the URL is the identity worth agreeing on rather than the name, since it is the key a generator can match on across independently authored schemas.
## The gap it fills A custom scalar declaration is a name and nothing else. `scalar GeoPoint` tells a reader — human or machine — that values of this type exist and are leaves. It says nothing about whether a `GeoPoint` is `"51.5074,-0.1278"`, an object, a geohash string or a pair of integers. That gap is uncomfortable, because a scalar is exactly where the interesting format decisions live. `@specifiedBy` is the specification's minimal answer: rather than inventing a format language, it lets you point at a document that already describes the format. ```graphql scalar DateTime @specifiedBy(url: "https://datatracker.ietf.org/doc/html/rfc3339") ``` ## What it is, precisely It is a **built-in type-system directive** taking one required argument, `url`, of type `String!`, and its only valid location is a scalar type definition. You do not declare it; every specification-conformant server already has it. It applies to custom scalars — the built-in five do not carry one, because their coercion is written into the specification itself. ## What it does not do This is the half of the answer interviewers are actually checking, and it is entirely negative: * The server does **not** fetch the URL — not at build time, not at request time, never. * It does **not** validate anything. A value that violates the linked document is accepted if the scalar's coercion accepts it. * It does **not** change coercion. Identical schemas with and without the directive execute identically. * It does **not** make the scalar interchangeable with another server's scalar of the same name. Two schemas can both declare `DateTime` with different URLs, or one with and one without. If the URL is wrong, stale or a 404, nothing anywhere fails. It is a signpost, and signposts do not enforce. ## Who reads it Three audiences, in ascending order of usefulness. **Humans reading the schema.** A reviewer who encounters `scalar Cursor` has to go ask someone. A reviewer who encounters a scalar carrying a URL follows it. That alone justifies the directive on any schema that outlives its authors. **Schema browsers and documentation tooling.** Because the URL is surfaced in introspection — on the scalar's type entry, as its specification URL — any tool reading the schema over the wire can render it as a link without special-casing anything. This is a small but genuine exception to the general rule that applied directives are not visible through introspection. **Client generators, in principle.** A generator configured to recognise a particular specification URL can map that scalar onto a real client-side type rather than an opaque value, and can do so by URL rather than by name — which is more robust, since names are arbitrary and collide across schemas while a URL identifies a format. In practice most generators are still configured by scalar name; the URL is the more principled key, and the directive is what makes keying on it possible at all. ## Where it sits in an interview Nobody's offer turns on this. It is asked out of curiosity, or as a probe: a candidate who knows it usually also knows that a custom scalar is otherwise opaque to clients, and that is the interesting conversation underneath. The clean answer is one sentence for what it attaches, one sentence for what it does not do, and one for who reads it. ## A warning about scope Be careful not to over-claim. `@specifiedBy` documents a *scalar's value format*. It is not a general-purpose documentation mechanism — descriptions are, and every SDL construct can carry one. It does not mark deprecation, does not carry a version, and does not apply to fields, arguments, objects or enums. One argument, one location, zero runtime behaviour.
- Does applying @specifiedBy change how the server coerces that scalar's values?Not at all. Coercion is whatever the server implemented for that scalar; the directive adds a URL and no behaviour. A schema with the directive and the same schema without it execute identically, accept the same inputs and produce the same outputs. If the linked document says one thing and the coercion does another, the coercion wins and nothing reports the discrepancy.
- Why would a client generator prefer keying on the URL rather than the scalar's name?Because names are arbitrary and collide. Two schemas can both call a scalar `Timestamp` and mean different formats, while a URL identifies the format itself. Keying on the URL lets a generator recognise a known format wherever it appears and map it to a proper client-side type, instead of relying on a per-schema, per-name configuration entry.
It is the 'see the manual' sticker on an appliance: it tells you exactly where the real instructions live and does absolutely nothing if you ignore it.
saying these in an interview costs you the question
- Thinks the server fetches or validates against the URL
- Believes it makes same-named scalars interchangeable
- Applies it to fields, objects or enums
- Confuses it with a description or a deprecation reason
- Assumes it changes the scalar's coercion behaviour