GraphQL Dataloader
Tags: graphql, dataloader, optimization, api
Published: 2021-06-18 03:43:47

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](https://github.com/graphql/dataloader) 

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*


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

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

type Author {
  id: ID!
  name: String!
  books: [Book!]!
}
``` 


*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)

```graphql
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
  }
}
```

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

```graphql
query {
  books {           # fetches books (1 query)
    title
    author {        # fetches author for each book (N queries for N book)
      name
    }
  }
}                   # N+1 round trips
```

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*

```javascript
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]);
          })
        };
      }
    });
```

Using defined Data Loader: You can grab defined DataLoader from the context
```graphql
Book: {
    author: async (root, args, context, info) => {
      return context.authorLoader.load(root.id);  // root of Book is Author
    }
  },
```

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](https://github.com/graphql/dataloader).

## 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:

```javascript
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()
```