skip to content

What does the @oneOf directive on a GraphQL input object type require, and is it in the specification?

level: middleimportance: nice to knowfreq 18%

answer

  1. A tagged choice, not a bag
  2. Applied to the input type itself
  3. Exactly one, and not null
  4. All fields nullable, no defaults
  5. Working draft, not the ratified edition

basics

~20 s

It marks an input object type whose caller must supply exactly one of its fields, with a non-null value. Every field of such an input must be nullable and carry no default. It is a working-draft addition, not part of the last ratified specification edition.

solid answer

~50 s

`@oneOf` is a type-system directive applied to an **input object type definition**. It says the input is a tagged choice: a caller must provide **exactly one** of its fields, and the value provided must not be null. For that rule to be meaningful the type system also constrains the input itself - every field must be nullable and must not carry a default value, so "provided" is unambiguous. Servers that implement it enforce the rule before execution, so a caller supplying two fields or none is rejected as an invalid request rather than reaching a resolver. On status: it is an addition that came through the specification's working draft and is not present in the October 2021 ratified edition, so support varies by server, by schema-validation tooling and by typed client generators. Before relying on it, check that every piece of the toolchain you ship through actually understands it; the long-standing alternative is an all-optional input with the rule enforced in server code and stated in the description.

code

graphql · 9 lines
graphql
input LockerLookupInput @oneOf {
  lockerId: ID
  siteCodeAndDoor: String
  parcelBarcode: String
}

type Query {
  locker(by: LockerLookupInput!): Locker
}

go deeper

for a junior

Recognize the marking when you meet it in SDL: it means pick exactly one of these fields for this call, rather than filling in as many as you like.

for a middle

Be able to state all three parts - exactly one field, a non-null value, and every field nullable with no default - and say that the check happens before any resolver runs.

for a senior

Show that you separate ratified specification from working draft from vendor support, and that you check server, pipeline and client generators before a published schema leans on a draft marking.

for a principal

Weigh the portability cost across a multi-consumer graph: a draft-status marking buys tooling-visible intent, but only where every consumer's generator understands it, and a hand-enforced rule may be the safer contract for now.

## What the directive marks `@oneOf` is applied to an input object type definition, not to a field and not to an operation: ```graphql input LockerLookupInput @oneOf { lockerId: ID siteCodeAndDoor: String parcelBarcode: String } type Query { locker(by: LockerLookupInput!): Locker } ``` The marking turns an ordinary bag of optional fields into a **tagged choice**: exactly one way of naming a locker, chosen per call. Without it, the type says only "here are three nullable fields", and nothing in the schema forbids a caller sending all three, or none. ## The rule, stated precisely Three parts, and candidates usually remember only the first: 1. A caller must supply **exactly one** of the input's fields - not zero, not two. 2. The value supplied for that field must **not be null**. Providing the field with an explicit null does not count as choosing it; it is an error. 3. The input type itself is constrained: **every field must be nullable and must have no default value**. That is what makes rule 1 checkable - a non-null field would have to be supplied always, and a defaulted field would be implicitly present on every call, so either would make "exactly one provided" incoherent. Rule 3 is a schema-level constraint, checked when the schema is built. Rules 1 and 2 are checked per request, before execution, so a violation is rejected as an invalid request rather than surfacing as a failure inside a resolver. ## Where the checking happens Because a caller can supply the value either as a literal in the document or through a variable, the enforcement has two halves. A literal input object written into the document can be checked when the document is validated. A value arriving in the variables map is only known at coercion time, just before execution. Either way the request does not run, and no resolver sees a half-chosen value - which is the entire benefit over enforcing the rule yourself. ## Specification status, and why it matters here This is the part of the question with teeth. `@oneOf` came through the specification's working draft and is **not** part of the October 2021 ratified edition - the most recent ratified one at the time of writing. Several servers implemented it ahead of ratification, and several did not. The practical consequence is a toolchain question rather than a philosophical one. Before a schema depends on it, three things need to understand it: the server that builds and validates the schema, any schema-validation or composition step in the pipeline, and the typed client generators your consumers run. A generator that does not know the marking will emit an all-optional shape, so your consumers get no compile-time help and the rule is enforced only by a runtime rejection. That is still better than nothing, but it is a different value proposition from the one people imagine. The long-standing alternative, which works everywhere, is an all-optional input whose exactly-one rule is enforced in server code and written into the type's description. It costs a hand-written check per input and it is invisible to tooling, which is precisely the gap the directive exists to close. ## What an interviewer is listening for Nobody's offer turns on this. What a good answer shows is a habit worth having: knowing the difference between what the specification ratifies, what lives in its working draft, and what a particular server happens to support - and saying which of the three you are relying on before you build on it.

  • Why must every field of a @oneOf input object be nullable and undefaulted?
    So that "provided" has a single meaning. A non-null field would have to be supplied on every call, and a defaulted field is effectively present on every call, so either would make "exactly one field provided" impossible to check. The constraint is enforced when the schema is built.
  • Does supplying one field with an explicit null satisfy the rule?
    No. The chosen field's value must not be null, so an explicit null is a violation rather than a choice. That is what stops a caller expressing "look this locker up by no identifier at all" while still technically naming one field.
  • How would you express the same constraint without the directive?
    Declare the input with all fields optional, state the rule in the type's description, and reject violations in server code before doing any work. It is portable and invisible to tooling: no explorer, generator or schema check knows the rule, so every consumer learns it from documentation or from an error.

saying these in an interview costs you the question

  • Thinks @oneOf applies to a field rather than the input type
  • Believes it allows zero or several fields to be supplied
  • Says an explicit null still counts as choosing a field
  • Assumes it is part of the last ratified specification edition
  • Expects every server and client generator to support it
  • Declares the input's fields non-null and expects it to work

context