Resolvers & Field Selection
22 min
A REST endpoint like GET /users/1 returns a fixed shape decided by the
server — every field, whether the client needs it or not. GraphQL
flips this: the CLIENT sends a query describing exactly the shape it
wants, and a resolver walks the underlying data, returning only
those requested fields. This function is the resolver's core job,
simplified to plain nested dicts instead of a real GraphQL schema.
def resolve(query, data):
result = {}
for field, subquery in query.items():
if field not in data:
continue
if subquery is True:
result[field] = data[field]
elif isinstance(subquery, dict):
if isinstance(data[field], list):
result[field] = [resolve(subquery, item) for item in data[field]]
else:
result[field] = resolve(subquery, data[field])
return result
Notice 'body' -- a potentially large field -- never gets fetched into the result at all, for either post, even though it exists in the underlying data. This is GraphQL's headline benefit: a mobile client that only needs titles and view counts never pays the cost of transferring full post bodies it won't display.
Write `resolve(query, data)`: `query` is a nested dict where each key maps to either `True` (return this scalar field as-is) or another dict (a nested selection to resolve recursively). If a field's value in `data` is a list, resolve the nested selection against EVERY item in that list. Return only the fields actually requested.
REST APIs' resources-verbs module covered fixed-shape responses (a GET to /users/1 always returns the whole user object). What's the core idea GraphQL changes about that?
You can implement field-selection resolution over nested data (including lists), the core mechanism that lets a GraphQL client request exactly the shape it needs.