By this point in the documentation, you know how to generate a GraphQL.js schema from the GraphQL schema language, and how to add resolvers to that schema to call functions. How do you access your backend from those resolvers? Well, it’s quite easy, but as your app gets more complex it might make sense to add some structure. We’ll start with the basics and then move on to more advanced conventions.
As you have read on the resolvers page, resolvers in GraphQL.js can return Promises. This means it’s easy to fetch data using any library that returns a promise for the result:
As you start to have more different resolvers that need to access the GitHub API, the above approach becomes unsustainable. It’s good to abstract that away into a “repository” pattern. We call these data fetching functions “connectors”:
Now, we can use this function in several resolvers:
This means we no longer have to worry about the details of fetching from GitHub inside our resolvers, and we just need to put in the right repository name to fetch. We can improve our GitHub fetching logic over time.
At some point, you might get to a situation where you are fetching the same objects over and over during the course of a single query. For example, you could have a list of repositories which each want to know about their owner:
Let’s say this is our resolver for
If the list of repositories has several that were owned by the same user, the
getAuthorByName function will be called once for each, doing unnecessary requests to the GitHub API, and running down our API limit.
You can improve the situation by adding a per-request cache with
One important thing to understand about
dataloader is that it caches the results forever, unless told otherwise. So we really want to make sure we create a new instance for every request sent to our server, so that we de-duplicate fetches in one query but not across multiple requests or, even worse, multiple users.
At this point, the code becomes a bit more complex, so we won’t reproduce it here. Check out the GitHunt-API example for the details:
- The GitHub connector, which uses DataLoader, passes along API keys, and does extra caching with GitHub’s eTag feature.
- The GitHub model, which defines some helpful functions to fetch users and repositories.
- The GraphQL context, which includes the models, initialized with the connector for every request.
- The resolvers, which use the model from the context to actually fetch the object.
The code is more decoupled than necessary for a small example, but it’s done that way intentionally to demonstrate how a larger API could be laid out.