Unlock the Power of GraphQL Resolvers
If you’re reading this, you’re likely already convinced of the benefits GraphQL brings to the table. One of the fundamental problems GraphQL solves is the overfetching and underfetching of data, particularly when building servers for mobile-first apps.
The Problem with REST
In REST, there are two ways to architect and design new requirements: create a new endpoint or reuse an existing endpoint by fetching extra information with it. Both approaches have their own set of tradeoffs. The first option leads to more round trips, which is not ideal if the mobile user is in a spotty network condition. The second option wastes bandwidth unnecessarily. GraphQL promises to give us exactly what we ask for, but only if we understand the quirks of GraphQL resolvers.
A Brief Review of Queries
Resolvers are functions that resolve the value for a GraphQL type or a field of a GraphQL type. Before diving into the resolver design process, let’s take a closer look at the GraphQL query type. GraphQL queries look like JSON, and everyone knows JSON well. Let’s design a GraphQL API for fetching data from a school database that has student and course information.
Visualizing Queries as Trees
The GraphQL query is parsed into a tree before hitting the resolvers. Visualizing queries this way is essential because efficient resolvers are designed based on the actual structure of the queries. The query is the root node, and student, name, courses, and title are the children.
Writing Resolvers
A resolver in GraphQL has the following structure: root, args, context, and info. Resolvers can be written for every type and every field. Let’s write a resolver for the Student type. Our schema file looks like this:
Understanding Resolver Arguments
Let’s explore the other arguments. The root argument is the result from the parent type. The context is a mutable object that can be used for storing/passing common configs like session data, req (in Express), etc. The info argument contains field information like fieldName, fieldNodes, returnType, etc.
Overcoming Potential Problems
There are scenarios in which we could experience overfetching because of the way we have designed our resolvers. For instance, if we write a resolver for “courses” in the student type and fetch the courses along with the student query, we might unintentionally force our server to overfetch. To overcome this, we can move the courses resolver to the courses field.
More Potential Problems
What happens if we write a query to fetch only for the courses? Again, we might run into overfetching. To solve this, we can move the student resolver down to its fields and resolve the fields separately.
Database Calls
Now that we’ve seen how to avoid some common problems while writing resolvers, let’s take a look at how we can structure the database calls and what options are available to us. If you’re using a SQL database, chances are you already use an ORM like sequelize or sqlalchemy for fetching data from your database. In the case of MongoDB, you can directly use the Mongo CRUD APIs inside the resolvers.
Final Thoughts
I hope you enjoyed reading this post about GraphQL resolvers, and hopefully, you found it useful. Remember, having an understanding of the problems resolvers solve is much more important than just writing them.