0

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));
Thjen
  • 527
  • 1
  • 9
  • 18

1 Answers1

3

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
});
Daniel Rearden
  • 80,636
  • 11
  • 185
  • 183
  • This works, thank you. Is it possible to refactor the QuestionLoader into generateLoaders without losing the new instance on each context? I have several other loaders that are not shown, and the context file would eventually get very big. – Thjen Feb 12 '20 at 09:03
  • 1
    Yes, the main point is to export a function that creates a loader instance instead of exporting a loader instance itself. If you want to call that function inside another function like `generateLoaders`, that's fine. – Daniel Rearden Feb 12 '20 at 11:18
  • This is causing me issues on my production server, but works locally. On production the dataloader doesn't seem to initialize (or inititalize in time) – A.com Oct 02 '20 at 06:03
  • @A.com please open a new issue with a complete code example. – Daniel Rearden Oct 02 '20 at 12:37
  • @DanielRearden completely was my fault... I had weird logic around my context object on a graphql server – A.com Oct 03 '20 at 00:09