Does the GraphQL specification define default property-based field resolution?
answer
- Read the execution section closely
- One step is assumed, not defined
- The type system supplies the function
- Portability across host languages is the reason
- Property lookup is convention, not specification
basics
~20 sNo. The specification treats each field's resolver as an internal function the type system provides and never says how it computes a value. Reading a same-named property off the parent value is a universal implementation convention.
solid answer
~50 sThe specification's execution algorithm is precise about collecting fields, coercing arguments, completing values and propagating errors - and at the single point where a raw value is obtained, it stops describing. It assumes the type system supplies an **internal resolver function** for each field and says nothing about where that function comes from or how it produces a value. The only value execution itself supplies is the initial value at the root of the operation. So same-name property lookup is convention, arrived at independently by every mainstream server, not a specified rule. That is deliberate: "read the property of the same name" is not portable across host languages where a parent value may be a class instance, a record, a map or a lazily-computed thunk, and leaving resolution open lets schema-first and code-first construction both satisfy the same spec. The cost is that the binding rule differs in detail between servers and appears nowhere in the SDL.
code
pseudocode · 6 lines# the specification's shape, paraphrased
ExecuteField(objectType, objectValue, fieldDefinition, variableValues):
argumentValues = CoerceArgumentValues(objectType, fieldDefinition, variableValues)
resolver = <internal function the type system provides for this field>
resolvedValue = resolver(objectValue, argumentValues)
return CompleteValue(fieldDefinition.type, resolvedValue, ...)go deeper
Know the headline: the default that reads a same-named property is how servers behave, not something the specification requires. Do not cite "the spec says" for it.
Explain which layer is specified - field collection, argument coercion, value completion, error propagation - and which is left open, and be able to say why a host-language binding rule could not be specified portably.
Use the distinction operationally: when a field returns unexplained nulls, the answer is in your server's binding rule, not the spec. Treat reliance on an unwritten default as an undocumented dependency.
Own the portability position. If a migration or a second implementation is plausible, decide whether implicit binding is allowed at all, since the SDL is portable and the resolver layer is not.
## What the specification actually says The GraphQL specification's execution section describes, in algorithmic steps, how a document turns into a response: collect the fields for the current object type, execute each one, complete the value against the field's declared type, assemble the response map. Inside the step that executes a single field, it calls out to obtain a raw value — and at that exact point the spec stops describing and starts assuming. It treats the resolver as an **internal function that the type system provides for that field**, takes it as given, and never specifies how the function is obtained or how it computes anything. That is the whole answer: **property lookup is not in the spec.** The spec defines the language, the type system's semantics, validation, and the shape of execution. It deliberately does not define how a field gets bound to host-language data. ``` # the spec's shape, paraphrased ExecuteField(objectType, objectValue, fieldDefinition, ...): argumentValues = CoerceArgumentValues(...) resolvedValue = <internal resolver the type system provides>(objectValue, argumentValues) return CompleteValue(fieldType, resolvedValue, ...) ``` The one value execution itself supplies is the initial value handed to the root operation type; from there on, every raw value comes out of a resolver the spec declines to define. ## Why leave it undefined Because "read the property of the same name" is not a portable instruction. GraphQL is implemented in languages where the parent value might be a class instance with private fields and getters, a struct with exported names, an immutable record, a hash map, a database row wrapper, or a lazily-evaluated thunk. A spec rule mandating property lookup would either be vague enough to be useless or specific enough to exclude most host languages. Leaving resolution to the type system also lets schema-first and code-first construction coexist: in one, resolvers are registered against a parsed SDL; in the other, the type system is built from host-language declarations and the binding is implicit. Both satisfy the same spec. There is a second, subtler reason. The spec's contract is about what a **client** can observe. A client cannot tell whether a field's value came from a hand-written resolver, a default property read, or a constant — the response shape is fixed by the document and the schema either way. Resolution is a server-authoring concern, and specifying it would constrain implementations without giving clients anything. ## What "convention, not spec" costs you Default resolution exists in every mainstream server, which makes it feel specified. The differences are in the details that bite: * **Name matching.** Whether a schema field `isExitRow` finds a member spelled `isExitRow`, `is_exit_row`, or a getter, and whether a boolean getter prefix is recognised, is per-implementation. * **Callables.** Some defaults invoke a same-named function they find; some return it, which then fails value completion. * **Map-like parents.** Whether a dictionary key counts as a member at all. * **Failure mode.** All of them agree that finding nothing yields null, not an error — which is why a binding difference surfaces as data-shaped nulls rather than a loud startup failure. So the portable artefact is the schema, not the server. Moving an SDL file plus a set of resolvers to a different implementation is a rewrite of the resolver layer, and the fields that were never written down — the ones the old default was quietly serving — are exactly the ones that break, because nothing in the SDL records that they were relying on a binding rule. ## The interview framing The reason this is worth knowing is not spec trivia; it is that it tells you where to look when a field returns null for no visible reason. If default resolution were specified, the answer would be in the spec. Because it is a per-implementation convention, the answer is in whatever binding rule your server applies, and the only durable defence is to stop relying on it for anything non-obvious: name the member explicitly, or write the resolver. A team that treats "the default will find it" as a spec guarantee has built on something no document promises. A candidate who can separate the specified layer — collection, execution order, value completion, error propagation — from the conventional layer around it is showing the thing this question is really probing: that they read GraphQL as a specification with an ecosystem on top, not as one library's behaviour.
- Why did the specification leave field resolution undefined instead of mandating property lookup?Portability. GraphQL is implemented in languages where a parent value might be a class with getters, an exported struct field, an immutable record, a dictionary key or a thunk; a mandated property rule would either be too vague to help or too specific to implement. Leaving resolution to the type system also lets schema-first wiring and code-first construction both conform, and clients cannot observe the difference anyway.
- What breaks when the same SDL and data model move to a different server implementation?Exactly the fields nobody wrote a resolver for. The SDL never recorded that they depended on a binding rule, and implementations differ on name matching, getter recognition, map-like parents and whether a same-named callable is invoked. The symptom is data-shaped nulls rather than a startup failure, because every default agrees that finding nothing yields null.
- Can a client tell whether a field was served by a default or a hand-written resolver?No. The response shape is fixed by the document and the schema, so the value looks identical either way. That is the deeper reason resolution is out of scope for the specification: it constrains server authors without giving clients any observable guarantee.
saying these in an interview costs you the question
- Claims the specification mandates same-name property lookup
- Names a spec directive that supposedly enables default resolution
- Says a field with no resolver is a specification violation
- Assumes every implementation matches names identically
- Thinks the spec defines getter or snake-case name matching