HomeToolsAbout

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 a Query entrypoint in the graph.

Connections in Graphql are directional.

  • Each field is node/edge in the graph

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) has City field defined
  • User Service would have a method getUser defined with an interface User 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 and email field calls its own corresponding resolver function
    • Context from User can be passed to either of the subnodes.

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 from Library (entrypoint) to Books to Author (in books) to get Author.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(); }, }
AboutContact