Article summary
In my last post, I described how you can build flexible APIs with GraphQL, using a family tree API as my demonstration. The implementation of the API was fairly simple, and I was able to traverse the graph of relationships between different people in the data set.
In this post, I’ll demonstrate how we can extend the same API to solve a problem with the first version. We can create new resolvers to fix a particular problem, but due to the nature of GraphQL, the same resolver can be used across many different queries. This unlocks more potential for the API without any extra development effort.
The Siblings Resolver
With the first verison of the family tree API, I could query for a person’s siblings by querying for the parents, then the parents’ children:
The result set includes the original person, who shouldn’t be considered a sibling to themselves. To get a better result, I added a new “sibling” resolver to the Person.
To do this, first I needed to add the field to the Person schema:
type Person {
// Exsiting Fields
id: Int
firstName: String
lastName: String
parents: [Person]
children: [Person]
// New Field
siblings: [Person]
}
Next, I need to add a resolver for this field:
// Some helper functions
const findParents = (context, person) => context.Person.findParents(person.id);
const getChildrenForParents = async (context, parents) => {
const children = flatten(await Promise.all(parents.map((parent) => context.Person.findChildren(parent.id))));
return distinct(children);
};
const resolvers = {
Person: {
// Existing resolvers
parents(person, args, context) {
return findParents(context, person);
},
children(person, args, context) {
return context.Person.findChildren(person.id);
},
// New resolver
async siblings(person, args, context) {
const parents = await findParents(context, person);
const parentsChildren = await getChildrenForParents(context, parents);
return exclude(person, parentsChildren);
}
},
// more resolvers here…
}
I wanted to keep this new resolver as simple as possible, so I did not write any new SQL to fetch siblings from the database. The new resolver uses only the existing implementation from the data layer. The less code I write in the data layer now, the less I’ll have to optimize in the future.
Now, I can write a simpler GraphQL query to get a person’s siblings:
Using this New Resolver for Other Queries
That wasn’t too much work to fix the siblings query. But we can do so much more with this query. We can use this siblings resolver for any person on this graph of people. I can query for uncles and aunts:
Or I can query for cousins:
Resolvers in GraphQL aren’t tied to any particular query. Carefully constructed APIs can take advantage of resolvers across many different use cases.
In my next post, I’ll demonstrate how we can make the GraphQL queries more efficient by optimizing the underlying queries. Until then, check out the project on GitHub.