skip to content

What is an inline fragment in a GraphQL query, and when do you need one?

level: juniorimportance: must knowfreq 71%

answer

  1. The field's type does not say enough
  2. Written once, right where it stands
  3. Unions share nothing but one meta-field
  4. The condition must be able to match
  5. Matching branches merge in flat

basics

~20 s

An inline fragment is an unnamed ... on SomeType { ... } block inside a selection set. You need one when a field returns an interface or a union and you want fields that exist only on one concrete type.

solid answer

~50 s

When a field's declared type is **abstract** — an interface or a union — the document cannot know which concrete object type will come back, so it cannot select that type's own fields directly. An **inline fragment** solves that: `... on Pallet { sscc }` says "if the object here turns out to be a `Pallet`, also select these fields". It is written straight into the selection set, has no name, and is used once where it stands. On an **interface** you may still select the interface's own declared fields without narrowing; on a **union** there are no shared fields at all, so everything except `__typename` must sit inside a fragment. The type condition must be able to overlap the enclosing type, or validation rejects the document. At execution, every branch whose condition matches the actual object type contributes — the matching fields land flat, as siblings of the outer ones.

code

graphql · 16 lines
graphql
query BinContents($binId: ID!) {
  bin(id: $binId) {
    label
    contents {
      __typename
      ... on Pallet {
        sscc
        grossWeightKg
      }
      ... on LooseCase {
        caseGtin
        quantity
      }
    }
  }
}

go deeper

for a junior

Be ready to write the ... on SomeType { } syntax from memory and say in one sentence why an abstract field needs it. Knowing that a union lets you select nothing but the type-name meta-field without narrowing is the detail that separates a rehearsed answer from a real one.

for a middle

Explain the mechanics: field collection includes every matching branch rather than picking one, matched fields merge flat into the parent object, unmatched ones are absent, and a type condition that cannot overlap the enclosing type is a validation error.

for a senior

Show judgement about the shape you hand clients: unions force every consumer to enumerate members, interfaces give them a guaranteed common selection. Be ready to say which you would choose for a field many teams read, and why.

for a principal

Own the consequence across teams: an abstract field is a contract about how much branching every downstream client must carry forever. Argue when a union's precision is worth that cost and when a shared interface, or a single type with nullable fields, keeps more clients working through change.

## The problem an inline fragment exists to solve A GraphQL document is written against the **schema**, but a field whose declared type is abstract — an interface or a union — does not tell the document what will actually be there. In a warehouse inventory graph, a bin's `contents` might be declared as a union of `Pallet`, `LooseCase` and `ReturnTote`. Every one of those has different fields. The document is static text; the concrete type is a runtime fact. So GraphQL splits the selection into a common part and conditional parts. The conditional part is a **fragment with a type condition**, and when you only need it once, in one place, you write it inline. ## Syntax and shape ```graphql query BinContents($binId: ID!) { bin(id: $binId) { contents { __typename ... on Pallet { sscc grossWeightKg } ... on LooseCase { caseGtin quantity } } } } ``` The `...` is the spread token, `on Pallet` is the **type condition**, and the braces hold a selection set like any other. There is no name and no definition elsewhere in the document — that is the whole difference from a named fragment, which is declared once and spread by name wherever it is needed. Choose inline when the branch is used in exactly one place; extract a named one when it is reused or when a client tool wants to own it. ## Interfaces and unions behave differently * An **interface** declares fields that every implementing type has. Those you may select directly on the abstract field, with no narrowing at all. You reach for an inline fragment only for the fields that exist on *one* implementation. * A **union** declares no fields whatsoever — it is only a list of possible object types. A selection set on a union may therefore contain only `__typename` and fragment spreads. Writing a plain field on a union is a validation error, even if every member happens to have a field by that name. That asymmetry is the single most common source of confusion, and it is worth being able to state without hesitating. ## The scope rule A type condition is not free-form. The specification requires that the condition's possible types and the enclosing type's possible types **overlap** — informally, that the fragment could ever apply where it is written. Narrowing `contents` to `... on Pallet` is fine because `Pallet` is a member of that union. Narrowing it to `... on Carrier`, a type that is not a member, is rejected during validation, before anything executes. The point of the rule is that a fragment which could never match is almost always a mistake, not a harmless no-op. A type condition may also be *wider* than the enclosing type, or equal to it. `... on InventoryNode { ... }` inside a selection on `Pallet` is legal if `Pallet` implements that interface — it just always matches. ## Branches are filters, not a switch statement This is where mental models go wrong. Inline fragments do not behave like a `switch` where exactly one arm runs. During execution the server determines the concrete object type at that position, then collects fields from the enclosing selection set **plus every inline fragment whose type condition matches**. If two branches both match — an interface branch and a concrete-type branch, say — both contribute, and their fields are merged. And the result is **flat**. The response object for a matching `Pallet` carries `sscc` and `grossWeightKg` as ordinary keys alongside `__typename`; there is no per-branch wrapper in the JSON: ```json { "__typename": "Pallet", "sscc": "00370412998415", "grossWeightKg": 612.4 } ``` Fields from a branch that did **not** match are simply absent — not present-and-null. A consumer that does an unconditional property read on a field it only asked for under one branch will find nothing there for the other members. ## What an interviewer is checking Three things, usually in one answer: that you know an abstract field cannot be selected concretely without narrowing; that you can name the difference between what an interface and a union let you select without a fragment; and that you know the matched fields come back flat, with the unmatched ones missing rather than null. If you can add that the type condition must be able to overlap the enclosing type, you are past the screening bar for this topic.

  • On a field whose type is an interface, which fields can you select without writing any inline fragment?
    Exactly the fields the interface itself declares, since every implementing type is guaranteed to have them. Anything an individual implementation adds must sit inside a fragment with that type as its condition. This is why interfaces degrade more gracefully than unions for clients: a shared shape is always available, whatever concrete type comes back.
  • What happens if two inline fragments in the same selection set both match the object being resolved?
    Both contribute. Field collection walks the selection set and includes every fragment whose type condition matches the concrete object type, then merges the results into one flat object. It is not a first-match-wins switch, so an interface branch and a concrete-type branch in the same selection can and do both apply.
  • When would you extract an inline fragment into a named fragment instead?
    When the same narrowing is needed in more than one place in the document, or when the selection is large enough that naming it makes the operation readable. Inline is the right default for a one-off branch; the two constructs have identical execution semantics, so the choice is purely about reuse and legibility.

It is the customs-declaration line "if this parcel is a pallet, also state its weight" — the extra questions only apply when the shipment turns out to be that kind.

saying these in an interview costs you the question

  • Thinking a union's shared-looking fields can be selected directly
  • Believing exactly one inline fragment branch ever runs
  • Expecting non-matching branch fields to come back as null
  • Confusing an inline fragment with a reusable named fragment
  • Assuming any type name is legal as a type condition
  • Thinking each branch nests its fields under its own key

context