Search code examples
node.jsexpressapolloapollo-serverdataloader

Dataloader caches across context creations (graphQl ApolloServer)


I have integrated DataLoaders into my graphQL requests, but for some reason the cache is memoized across requests. I can read that passing a function to context in the ApolloServer config should create a new context on each request, but for some reason the DataLoaders are memoized even then.

This is my code

Apollo Config:

export default new ApolloServer({
  resolvers,
  typeDefs,
  context: ({ req, res }) => generateContext(req, res)
});

generateContext:

const generateContext = (req: Express.Request, res: Express.Response) => ({
  ...generateLoaders(),
  res,
  req
});

generateLoaders:

import * as questionLoaders from './questionLoaders';

const generateLoaders = () => ({
  questionLoaders
});

questionLoader:

const batchQuestions = async (ids: number[]) => {
  const questions = await Question.query().findByIds(ids);
  return ids.map((id) => questions.find((q) => q.id === id));
};

export const questionLoader = new dataloader((ids: number[]) => batchQuestions(ids));

Solution

  • You only call the DataLoader constructor once and then export the resulting DataLoader instance as a constant. Even though your context function is called on each request, it uses the imported value, which is always the same DataLoader instance. Change your export so that it is a function:

    export const createQuestionLoader = () => new dataloader((ids: number[]) => batchQuestions(ids));
    

    You can then import it and call the function inside context:

    import { createQuestionLoader } from '...'
    
    const generateContext = (req: Express.Request, res: Express.Response) => ({
      QuestionLoader: createQuestionLoader(),
      res,
      req
    });