Resolvers
What is it
Graphql resolver is a middleware
that converts a request
to a call to some data provider that will retrieve the information requested.
- Each resolver function is responsible for fetching the data for a specific
field
in a GQL query.
The return type
of each resolver function ideally complies to the type defined on the field
/node
.
What it means to be a Graph
Query
is the base (top most) field/node of every query.
- every
query
starts with aQuery
entrypoint in the graph.
Connections in Graphql are directional.
- Each field is
node/edge
in thegraph
Nested field (connected nodes) can retain context
If we're getting User's City
, User -> Address
connection would be established by passing User's Id
value to Address
resolver which would retrieve details like City
by calling its respective service method.
- This would be possible because the
Address
type (as a node) hasCity
field defined User Service
would have a methodgetUser
defined with an interfaceUser
which alone should satisfy the node.
User {
...user,
address,
email
}
User
graph node may extend to other fields like Address
and Email
- These are separate nodes on a graph that can be established through a resolver
- Each of the
address
andemail
field calls its own corresponding resolver function - Context from
User
can be passed to either of the subnodes.
- Each of the
Default Resolver
When a field does not have a specified resolver
to match the field name, a default resolver
will look in root
to find a property with the same name as the field.
Regardless of whether the default resolver has the matching field or not, the inner fields are evaluated as a distinct resolver.
- If both default resolver and the inner field resolvers both define a field return value, inner field resolver return value is returned.
To override inner field resolver, you need to reference the default resolver value in the inner resolver using root
.
const resolvers = { Book: { title: (root, args, context) => { // when no default value provided, will return "inner field" // reference to root here will result in "outer root" return root.title || "inner field"; }, content: (root, args, context) => { return "inner field overrides outer default"; } }, Query: { // getBook's return is typed to Book type getBook: (root, args, context) => { return { title: "outer root", content: "outer default will never return because inner exists" } } } }
Nested Fields (Resolver Chain)
Nested Field defined on a resolver is available because of the resolver chain
.
- There is no such thing as "nested resolver"
- Each resolver is independent of other resolver functions
Query.libraries() > Library.books() > Book.author() > Author.name()
- Chain of passing down
arg
values fromLibrary
(entrypoint) toBooks
toAuthor
(in books) to getAuthor.name
.
# schema schema { query: Query } type Query { library: Library } type Library { books: Books } type Books { authors: [Author] } type Author { name: String }
// resolver { Query: { library: () => getLibrary(); }, Library: { books: (parent, args, contextValue, info) => getBooks(parent); }, Books: { authors: (parent, args, contextValue, info) => getAuthors(parent); }, Author: { name: (parent, args, contextValue, info) => getName(); }, }