skip to content

What does `extend type` do in GraphQL SDL, and what may an extension not do?

level: middleimportance: should knowfreq 47%

answer

  1. Adding to something already declared
  2. One keyword across every definition kind
  3. Additive only, never a rewrite
  4. What happens on a name collision?

basics

~20 s

It adds fields, interfaces or directives to a type already defined elsewhere in the same type system, without touching the original definition. Extensions may only add: redeclaring an existing field is a validation error, never an override.

solid answer

~50 s

`extend type Seat { upgradeEligible: Boolean! }` means “there is already an object type called `Seat`; add this to it”. Every definition kind has an extension form — `extend interface`, `extend union`, `extend enum`, `extend input`, `extend scalar` and `extend schema`, the last of which adds a root operation type or a directive to the schema definition. The rules are strict and additive: the named type must already exist and be the same kind, a field name may not be declared twice across the original and all its extensions, a non-repeatable directive may not be applied twice, and an interface added by an extension must be satisfied by the assembled type. So an extension cannot change a field's type, remove anything, or shadow an earlier declaration — a collision is a build failure, not a last-one-wins merge. It also creates no new type: there is still one `Seat`.

code

graphql · 13 lines
graphql
type Seat {
  number: String!
  cabin: CabinClass!
  blocked: Boolean!
}

extend type Seat {
  upgradeEligible: Boolean!
}

extend schema {
  subscription: SeatMapSubscription
}

go deeper

for a junior

Know the shape: extend type Seat { ... } adds to a type declared elsewhere, and every definition kind has the same form. Being able to read one in a codebase is enough at this level.

for a middle

Explain the mechanics: the type must already exist and match kind, additions cannot collide with an existing field, and the result is one merged type rather than a subtype. Be ready to say what happens on a duplicate.

for a senior

Demonstrate why additive-with-error is the useful property: several teams can write into one type without any of them being able to silently redefine another's field. Know that the guarantee holds regardless of load order.

for a principal

Own where the document boundaries go. Extensions let file layout follow ownership, but they also hide provenance from every consumer, so the ownership record has to live outside the schema in review rules rather than in the SDL.

## The construct `extend type Seat { ... }` is a statement about a type that already exists: somewhere in this type system there is an object type called `Seat`, and these fields, interfaces or directives belong to it too. After assembly there is exactly one `Seat`, carrying everything its original definition and all of its extensions declared. Every definition kind has an extension form, and they all work the same way: * `extend type` — adds fields, interfaces, directives to an object type * `extend interface` — adds fields, interfaces, directives to an interface * `extend union` — adds members * `extend enum` — adds values * `extend input` — adds input fields * `extend scalar` — adds directives * `extend schema` — adds root operation types or directives to the schema definition ## Why the specification has it The stated motivation is representing a type system that has been extended from an original one: a caller adding fields it only knows about locally, or a service whose schema is genuinely an extension of another description. The everyday use is more mundane — one service splitting a large schema by concern so that a document boundary follows an ownership boundary. A seating document defines `Seat`; a loyalty document adds the one field loyalty cares about; nobody edits anyone else's file. ```graphql # seating type Seat { number: String! cabin: CabinClass! blocked: Boolean! } # loyalty extend type Seat { upgradeEligible: Boolean! } extend schema { subscription: SeatMapSubscription } ``` ## The rules an extension must satisfy Type-system validation enforces roughly this set: 1. **The named type must already be defined, and be the same kind.** You cannot extend a type nobody declared, and you cannot extend an object type with an `extend interface`. 2. **No duplicate field names.** A field declared by the extension must not already exist on the original type or on any other extension of it. 3. **No duplicate non-repeatable directives.** If the original already carries a non-repeatable directive, an extension may not apply it again. 4. **Added interfaces must not already be implemented, and must be satisfied.** If the extension says `implements Bookable`, the assembled type — original plus every extension — must declare every field `Bookable` requires, with matching arguments. 5. **The result must be a valid type of its kind**, judged after assembly rather than per document. ## Additive only — there is no override This is the point interviews turn on. If the seating document declares `blocked: Boolean!` and a second document writes `extend type Seat { blocked: Boolean }`, hoping to relax the nullability, the build fails. It does not take the later declaration, it does not take the earlier one, and it does not depend on which document was read first. Extension is *addition with collision as an error*, which is what makes it safe for several teams to write into one type: nobody can silently change the meaning of somebody else's field. The same applies to `extend schema`. Adding a subscription root that is already declared is an error, not a replacement. ## Not inheritance, not a subtype An extension produces no new type. There is no `Seat` and `ExtendedSeat`; there is one `Seat`, and every field that returns a `Seat` — including ones written before the extension existed — now sees the added fields. Candidates who describe `extend` as a subclassing mechanism are reaching for the wrong model entirely: GraphQL's shared-contract mechanism is the interface, and `extend` is closer to filling in a form that has already been started. ## Extensions carry no description of their own The grammar gives a type extension no description slot. Documentation of the type belongs to its original definition. Fields declared *inside* an extension do carry their own descriptions normally — it is only the extension as a whole that cannot be described. Practically that means the extending team can document what it added but cannot re-document the type. ## Where extensions go Extensions are resolved when the type system is assembled, before any operation runs. Introspection shows the merged type and nothing about provenance: a client cannot tell that `upgradeEligible` arrived from a different document than `number`, and there is no per-request cost — a peak of 1,200 requests a minute pays nothing for however many extensions the schema was built from. ## What `extend` is not for It is not a mechanism for combining several *services* into one graph; that is a separate composition problem with its own machinery. It is not a versioning device — you cannot extend your way to a changed field. And it is not a way to make a type conditional: everything an extension adds is unconditionally part of the schema every client introspects.

  • Two documents both add a field named `notes` to the same object type. What happens?
    Type-system validation fails and the schema does not build. Extensions are additive with collision treated as an error — there is no last-one-wins rule, so the outcome is the same whichever document is read first. One of the two has to rename.
  • Can a type extension carry its own description?
    No — the extension grammar has no description slot, so the type's documentation stays with its original definition. Fields declared inside the extension do carry descriptions normally, which is usually all the extending team needs.
  • Does `extend type` create a new type that clients can distinguish?
    No. There is exactly one type after assembly, and introspection shows the merged result with no record of which document contributed which field. Anything an extension adds is part of the schema for every client.

A type extension is another page slipped into a binder that already exists, not a photocopy of the binder with edits. There is still one Seat, and two pages claiming the same heading is a filing error rather than a decision about which one wins.

saying these in an interview costs you the question

  • Says extend type creates a subtype or child type
  • Believes an extension can change an existing field's type
  • Expects the last document to win on a duplicate field
  • Thinks extensions are how separate services are combined
  • Assumes clients can see which document added a field
  • Puts a description on the extension itself

context