What does a GraphQL server do for a schema field that has no resolver of its own?
answer
- Nobody writes code for every field
- Something must still run per field
- Look at what the parent already returned
- Same-named member, read and returned
- Computed and argument-taking fields need code
basics
~20 sIt uses a default field resolver: it reads a member of the same name off the parent value the field above already resolved - a property, getter or map key - and returns it unchanged. It performs no I/O.
solid answer
~50 sExecution must produce a value for every field the document selected, so servers ship a **default field resolver** for fields the type system has no resolver registered for. It looks up a member with the same name as the schema field on the **parent value** - the object the field one level up resolved - and returns it as-is. That covers most of a schema: in an airline seat-map graph, `Seat.designator`, `Seat.cabin` and `Seat.isExitRow` are already members of the loaded seat record. Four families still need code: computed fields such as `Seat.isBlockedForCrew`, fields whose backing member is spelled differently, fields that take arguments (the default ignores them), and fields whose data lives in another source. Finding no member is not an error - the field completes as null - so an unwired nullable field looks exactly like absent data. Property lookup is a universal convention, not a rule in the specification.
code
graphql · 13 linestype Seat {
designator: String!
cabin: Cabin!
isExitRow: Boolean!
isBlockedForCrew: Boolean!
currentHold: SeatHold
}
enum Cabin {
ECONOMY
PREMIUM
BUSINESS
}go deeper
Recall the one-sentence rule: with no resolver registered, the server reads a member of the same name off the parent value and returns it. Be able to point at two schema fields and say which one needs code.
Explain the mechanics - what counts as the parent value, that lookup covers properties, getters and map keys, that no I/O happens, and that a missing member becomes a silent null rather than an error.
Show that you use the rule as a boundary: the fields you hand-write are the computed, renamed, argument-driven and remotely-backed ones. Be ready to diagnose an all-nulls field as a binding problem rather than a data problem.
Own the consequence for portability and review. Because the binding rule is per-implementation and never appears in the SDL, an unwritten default is an undocumented dependency; decide as a team whether the default is allowed to serve anything beyond plain stored members.
## What execution needs, field by field GraphQL execution walks the selection set of an operation and, for **every field selected in the document**, has to produce a value. There is no "skip" branch: if the client asked for `designator`, a value for `designator` must appear under that response key. So execution needs, per field, some function it can call to obtain a raw value before that value is checked against the field's declared type. Hand-writing that function for every field in a large schema would be absurd. In an airline seat-map graph, a `Seat` type might declare `designator`, `cabin`, `row`, `column`, `hasPower`, `isExitRow` — and all six of those are already sitting on the seat record the parent field loaded. Writing six functions that each say "return the member of the same name" is pure ceremony. ## The fallback rule So every mainstream server ships a **default field resolver**: when the type system has no resolver registered for a field, execution uses a generic one that looks up a member of the same name on the **parent value** — the value the field one level up already resolved — and returns it unchanged. "Member of the same name" is deliberately loose, because the parent value is whatever the host language happens to hold: an object with a property, a record with a getter, a dictionary or map with a key, a row-like structure. The default resolver's job is to bridge the schema's field name to that member. Many implementations add one more step: if the member it finds is itself callable, the default invokes it rather than returning the function value. That is a convention, and it varies, so it is not something to design a schema around. ``` function defaultResolve(parentValue, fieldName, args): if parentValue is null: return null member = lookupMember(parentValue, fieldName) # property, getter, or map key if member is absent: return NOTHING # completed as null if member is callable: return call(member) # common, not universal return member ``` Two things this rule does **not** do are worth stating plainly. It performs no I/O — it never queries a database, calls a service, or reads a cache; it only reads memory it was handed. And it makes no use of the field's arguments, so a field that means anything by its arguments needs code of its own. ## Which fields still need a resolver Four families fall outside the rule: * **Computed or derived fields.** `Seat.isBlockedForCrew` is not stored on a seat record; it is a decision made from the aircraft's crew-rest configuration and today's roster. No member of that name exists to read, so without a resolver the field yields null. * **Fields whose backing member is named differently.** The schema says `designator`; the loaded record calls it `seatLabel`. The default looks for `designator`, finds nothing, and produces null. * **Fields that take arguments.** Arguments are validated and coerced by execution and then passed to the resolver, and the default ignores them entirely. * **Fields whose data lives somewhere else.** `Seat.currentHold` may require a lookup against a hold store. Nothing on the parent value can answer it. ## What happens when there is nothing to read This is the part that surprises people, because it is quiet. Default resolution finding no member is not an error — it simply produced no value, which is completed as `null`. If the field is nullable, the client gets `"designator": null` with **no entry in the response's errors list at all**. The typo, the renamed column, the field someone added to the SDL and forgot to wire — all of them look like legitimately absent data. Only a Non-Null declaration turns the silence into a field error. ```graphql type Seat { designator: String! # read straight off the loaded seat record cabin: Cabin! # same isBlockedForCrew: Boolean! # computed - needs its own resolver } ``` ## It is a convention, not a specified rule The specification does not describe property lookup. It treats each field's resolver as an internal function the type system provides and says nothing about how that function computes a value; default resolution is the near-universal answer implementations chose independently. The practical consequence is that the rule is *similar* everywhere and *identical* nowhere: name matching, getter recognition, map handling and callable invocation all differ in detail. SDL that resolved perfectly under one server can come up full of nulls under another with no schema change at all, because the schema never carried the binding rule. ## How to talk about it The useful framing in an interview is that default resolution defines the **boundary of hand-written code** in a GraphQL server. The fields you write resolvers for are exactly the fields that are computed, renamed, argument-driven, or backed by a different source. Everything else is the parent value already in hand, read by name.
- Which fields in a schema will default resolution never serve correctly?Four kinds. Computed or derived fields, because no member of that name exists on the parent value. Fields whose backing member is spelled differently from the schema field. Fields that take arguments, since the default ignores them. And fields whose data lives in another store or service, which the default cannot reach because it performs no I/O. Each of those needs a resolver written for it.
- A nullable field has no resolver and the parent value has no member of that name. What does the client see?A null under that response key and nothing in the errors list. Default resolution finding no member is not a failure - it simply produced no value, which value completion turns into null. That is why a typo in the SDL or a renamed backing member looks identical to legitimately missing data. Declaring the field Non-Null is what converts the silence into a field error.
- Does the default resolver ever hit a database?No. It only reads memory it was already handed - the parent value. Any field whose answer requires a query, a service call or a cache read needs its own resolver. This is also why one loaded aggregate can feed a deep subtree at zero extra cost: every field under it is a member read, not a fetch.
saying these in an interview costs you the question
- Says every field in a schema needs its own resolver function
- Thinks the default resolver queries the database by field name
- Claims the GraphQL specification mandates same-name property lookup
- Expects a computed field to work with no resolver written
- Assumes the default resolver applies the field's arguments
- Believes a missing member raises an error on a nullable field