DEV Community

loading...
Cover image for GraphQL Dataloader

GraphQL Dataloader

ldsrogan
I'm super interested in cool technologies!
・3 min read

Using GraphQL gives simpler and lighter ways to manage APIs from the backend service if using correctly. One of the ways to use GraphQL correctly is to use GraphQL Dataloader.

What is Dataloader?

DataLoader is a generic utility to be used as part of your application's data fetching layer to provide a simplified and consistent API over various remote data sources such as databases or web services via batching and caching.

dataloader github-page

To understand the advantage of using Dataloader, let’s look at the simple example.

Example

Let's say that we have the following GraphQL type definitions.

Schema

type Query {
  book(id: String!): Book!
  authors: [Author]!
}

type Book {
  id: ID!
  title: String!
  author: Author!
}

type Author {
  id: ID!
  name: String!
  books: [Book!]!
}
Enter fullscreen mode Exit fullscreen mode

Resolver

FYI

root — Result from the previous/parent type

args — Arguments provided to the field

context — a Mutable object that is provided to all resolvers

info — Field-specific information relevant to the query (used rarely)

Query: {

  book: (root, args, context, info) => {
    // using args.id to query data from db
  },
  authors: (root, args, context, info) => {
    // query authors data from db
  }
},
Book: {
  author: (root, args, context, info) => {
    // using args.id to query author data from db
  }
},
Author: {
  books: (root, args, context, info) => {
    //using args.id to query books from db
  }
}
Enter fullscreen mode Exit fullscreen mode

As you see in the example above, Book and Author types pass their id to their related children node. In this case, Book’s child node is Author, and Author’s child node is multiple Book nodes.

By doing so, you don’t always have to access the author table even if the client doesn’t query the author in the book.

N+1 Problem and DataLoader

The n+1 problem means that the server executes multiple unnecessary round trips to datastores for nested data.

N+1 Problem Example

query {
  books {           # fetches books (1 query)
    title
    author {        # fetches author for each book (N queries for N book)
      name
    }
  }
}                   # N+1 round trips
Enter fullscreen mode Exit fullscreen mode

In the above case, the server makes 1 round trip to a datastore to fetch the books, then makes N round trips to a datastore to fetch the author for N authors.

Using DataLoader

DataLoader is a generic utility to be used as part of your application's data fetching layer to provide a simplified and consistent API over various remote data sources such as databases or web services via batching and caching.

Setup Apollo Server with DataLoader

const server = new ApolloServer({
      typeDefs,
      resolvers,
      context: () => {
        return {
          authorLoader: new DataLoader(async keys => {
            const authors = // query authors from database
            const authorMap = {};

            authors.forEach(author => {
              authorMap[author.id] = author;
            });

            return keys.map(key => authorMap[key]);
          })
        };
      }
    });
Enter fullscreen mode Exit fullscreen mode

Using defined Data Loader: You can grab defined DataLoader from the context

Book: {
    author: async (root, args, context, info) => {
      return context.authorLoader.load(root.id);  // root of Book is Author
    }
  },
Enter fullscreen mode Exit fullscreen mode

The basic idea is to create a batch to combine all fetching processes as one process and use the cached data for duplicated data.

Caching

Once .load() is called, the result is cached and will be used to avoid the duplicated call for the value with the keys that the .load() used.

Clearing Cache

Rarely there is a case when we need to clear the cache manually.

For example, when some values are updated with the same key within the same request, the cached value may be outdated and need to be cleared. In this case, we can simply use .clear() or .clearAll().

For more information about Caching, please visit the GraphQL DataLoader GitHub page.

Using DataLoader with Authorization and Authentication

The request may be separated by the different tenant or the different credential, and their request may be mixed and produced the wrong response. Thus, we recommend creating a Data Loader instance per request.

For example, when using with express:

function createLoaders(authToken) {
  return {
    users: new DataLoader(ids => genUsers(authToken, ids)),
  }
}

const app = express()

app.get('/', function(req, res) {
  const authToken = authenticateUser(req)
  const loaders = createLoaders(authToken)
  res.send(renderPage(req, loaders))
})

app.listen()
Enter fullscreen mode Exit fullscreen mode

Discussion (0)