In GraphQL, an async resolver function resolves a query, mutation or a single field on a GraphQL type. Each resolver function is passed 3 arguments:
- object – the object you are resolving
- args – the args passed to the field or query
- context – a context for resolving queries, mutations or fields
The context argument can contain any information you put in it when starting to resolve a top level query or mutation in graphql. Most of the time, this context contains DataLoaders, or a database connection or authentication information.
Typically the async resolver function will use the context to connect to a database or REST api, get or modify the data using the database or REST api and then return the necessary information.
Include graphql In The graphql Resolver Context
Recently I did something that ended up being very useful for some of the complex work we were working on. I added graphql to the context used when resolving a function in graphql. Doing so made it possible to call graphql while resolving a graphql request and to make use of the same DataLoaders and other context information.
An Example – copyToDoList
To see the value in having graphql be able to call graphql, lets consider a simple ToDo list application and API. Each User can have a ToDoList. Each ToDoList can have ToDoItems. When resolving a mutation to copyToDoList(id), it might be easiest to simply query for the existing ToDoList using the existing graphql query getToDoList(id), remove all the ids from the returned ToDoList and all the associated ToDoItems and then save the ToDoList and all the ToDoItems with no ids under a new name by calling the mutation to saveToDoList(ToDoList) which would save the list and list items and assign ids to all the entities and return the result.
We just implemented the copyToDoList mutation resolver by calling the getToDoList graphql query, then calling the saveToDoList graphql mutation and returning the result.
There are a number of benefits of enabling this type of functionality inside graphql resolvers. First, it gives you the option to implement a resolver function by calling your own graphql queries or mutations, if that is the fastest or easiest way to do it. Second, it allows your resolver function to deal with external names for entities and fields instead of the underlying names from database tables or column. Third, it makes it possible to leverage existing graphql resolvers. Finally, it compounds the performance savings by sharing the same DataLoaders for any queries or mutations you call while resolving which could result in fewer round trips to the database or REST api to get or modify the underlying data.