Understanding GraphQL Resolvers

Cover Image for Understanding GraphQL Resolvers

Let's start by talking about what a resolver is. A GraphQL Resolver is a function that is responsible for populating the data for a single field in your schema. What does this mean? Let's try to break it down, here is some data first:

1// Data in memory
2let person = {
3 name: 'Matt',
4 hobbies: [
5 { name: 'walking' },
6 { name: 'cycling' }
7 ]
8}

We have a simple object which contains a name property and an array of hobbies. Given the following Schema and initial query resolver, we can see how these resolvers work.

1// GraphQL Schema
2type Query { person: Person }
3type Person { name: String! hobbies: [Hobbies] }
4type Hobbies { name: String! }
5
6const resolvers = {
7 Query: {
8 person (parent, args, context) {
9 return person
10 }
11 }
12}

We define the schema with the type keyword, Query being the main entry point for all our GraphQL queries. The Query has one property person which then references the Person definition, so we can now query for the "name" & "hobbies" properties. The return person within our resolver is the data that in our instance is in memory, but this could also be a database lookup or even an API request. Given the following query:

1query { person { name } }
2// output
3
4{
5 data: {
6 person: {
7 name: 'Matt'
8 }
9 }
10}

The entire data object will be loaded, but only what was requested which was the person.name will be returned.

But say we wanted to do something extra for the hobbies property, we could do this within a resolver, for example, add an image URL. We would need to create a resolver for that property and it would look something like, which would be a specific resolver for the hobbies when queried within the person context:

1const resolvers = {
2 Query: {
3 person (parent, args, context) {
4 return person
5 }
6 },
7 Person: {
8 hobbies (parent, args, context) {
9 return person.hobbies.map(hobby => {
10 return {
11 ...hobby,
12 url: `https://hobbies.com/${hobby.name}`
13 }
14 })
15 }
16 }
17}

Don't forget to update the type definition for Hobbies:

1type Hobbies { name: String! url: String }

We can also be more generic and have a resolver for the url property on the Hobbies definition, which would look something like:

1const resolvers = {
2 Query: {
3 person (parent, args, context) {
4 return person
5 }
6 },
7 Hobbies: {
8 url (parent, args, context) {
9 return `https://hobbies.com/${parent.name}`
10 }
11 }
12}

This gives you the flexibility if you want to have the resolver for that property always do the same thing, or if you want it to do something more specific when fetched within a specific parent property context.

You can have the two resolvers together but the top-level Hobbies one will resolve last and as such will take precedence.

Thanks for reading!


Published by Matt Chaffe

Popular Stories