2

I'm having a hard time figuring out why my graphQL & DataLoader setup isn't working and could use some help.

I have a User and a Orchestra type, and I would like to transform a User to populate its createdOrchestras field and do the same thing with Orchestra and an owner field.

EDITED. The following code causes an infinite loop:

Here are the DataLoaders, which are passed to the resolvers via context:

const DataLoader = require('dataloader');
const Orchestra = require('../models/orchestras');
const User = require('../models/users');

const batchOrchestras = async ids => {
   const orchestras = await Orchestra.find({ _id: { $in: ids } });

   const orchestraMap = {};
   orchestras.forEach(o => {
      orchestraMap[o.id] = o;
   });

   return ids.map(id => orchestraMap[id] || new Error(`No result for ${id}`));
}

const batchUsers = async ids => {
   const users = await User.find({ _id: { $in: ids } });

   const userMap = {};
   users.forEach(u => {
     userMap[u.id] = u;
   });

   return ids.map(id => userMap[id] || new Error(`No result for ${id}`));
 };

module.exports = () => new DataLoader(batchUsers) 
module.exports = () => new DataLoader(batchOrchestras);

Here are the transform functions which should be capable of fetching data for nested fields via data loaders and modify sensitive fields like the user password.

async function transformUser(userId, loaders) {
  const user = await loaders.userLoader.load(userId.toString());

  return {
    ...user._doc,
    createdOrchestras: await Promise.all(
      user._doc.createdOrchestras.map(orchestra => 
        transformOrchestra(orchestra, loaders)
      )
    )
  }
}

async function transformOrchestra(orchestraId, loaders) {
  const orchestra = await loaders.orchestraLoader.load(orchestraId.toString());

  return {
    ...orchestra._doc,
    owner: transformUser(orchestra._doc.owner, loaders)
  }
}

module.exports = {
  transformUser,
  transformOrchestra
}

How should I restructure the code to prevent an infinite loop but keeping the transform functions as the final providers of data for a particular field ?

  • Are there not any additional errors shown in your server's output? If `transformUser` and `transformOrchestra` return a Promise, something could be going wrong since you're using `forEach`. You'd use `map` with `Promise.all` if you needed to await a function that returned a Promise. – Daniel Rearden Jan 13 '20 at 12:38
  • Hi daniel, I edited the issue because I needed to get my loaders from context. I used your suggestion of wrapping multiple callbacks in a Promise.all and it makes sense, although I'm triggering an infinite loop. – Lorenzo Rivosecchi Jan 13 '20 at 13:48
  • You should put this logic inside your resolvers, not your loaders. This way it will only be called when the field is actually requested. Also, you're overwriting your `module.exports` in the first file. – Daniel Rearden Jan 13 '20 at 14:04

0 Answers0