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
fieldin 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
querystarts with aQueryentrypoint in the graph.
Connections in Graphql are directional.
- Each field is
node/edgein 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
Addresstype (as a node) hasCityfield defined User Servicewould have a methodgetUserdefined with an interfaceUserwhich 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
addressandemailfield calls its own corresponding resolver function - Context from
Usercan 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
argvalues fromLibrary(entrypoint) toBookstoAuthor(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(); }, }