npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@natac13/mongoose-dataloader

v3.1.3

Published

Two ways to use Dataloader with Mongoose for MongoDB

Readme

Mongoose-Dataloader

A way to use Dataloader with Mongoose for MongoDB

Install

npm install -S @natac13/mongoose-dataloader

Usage

import createOneToOneMongooseLoader from '@natac13/mongoose-dataloader/dist/createOneToOneMongooseLoader';
import createOneToManyMongooseLoader from '@natac13/mongoose-dataloader/dist/createOneToManyMongooseLoader';

const modelLoader = createOneToOneMongooseLoader(Model, '_id');
const doc = modelLoader.load(id);

const arrayOfModelsLoader = createOneToManyMongooseLoader(Model2, 'modelOneID');
const arrayOfDocs = arrayOfModelsLoader.load(id);

Parameters

Both functions take the same parameters.

| param | required | default | Description | | :------------: | :------: | :---------------------------------------: | :-----------------------------------------------------------------------------: | | model | true | none | Mongoose Model | | field | false | _id | Field to query against | | options | false | { lean: true, projection: {} } | Mongoose options | | dataLoaderOpts | false | { cacheKeyFn: (key) => key.toString() } | Dataloader options |

Example

Using Apollo Server, GraphQL and MongoDB. This example uses Courses that have associated Classes. But similary it could be thought of as Publishers(Courses) that have assciated Books(Classes); like the linked MongoDB Doc below.

Say we have a Training Center that offers different Courses. To train someone in a Course, a Class is put on with on a certain date, and will reference the Course. Therefore the list of Classes will be a growing array without a limit. From the MongoDB docs

When using references, the growth of the relationships determine where to store the reference. If the number of books per publisher is small with limited growth, storing the book reference inside the publisher document may sometimes be useful. Otherwise, if the number of books per publisher is unbounded, this data model would lead to mutable, growing arrays.

Therefore a Class will have a reference to the Course Document.

Now given a query for a list of Classes the GraphQL resolver will first find all the Classes then (without Dataloader) there will be n calls to the DB to fetch the respective Course document. Which is the 1 + n problem that Dataloader addresses. Not only are there n calls, which can be batched, most of the returned Courses will be duplicates. Say the query was for all Classes with a specific courseId

query GetClasses($courseId: ID!){
  getClasses(courseId: $courseId) {
    id
    dateOf
    course {
      id
      name
    }
  }
}

Since we are only querying for Classes for the same Course all the n calls will be redundant! This is where Dataloader shines! It will batch up the n calls and query the DB once as well as 'cache' the response for the duration of the single request.

Apollo Server setup.

// dataSources will will called on each request and put on the context
const dataSources = () => ({
  Course: CourseModel,
  Class: ClassModel,
  CourseLoader: createOneToOneMongooseLoader(Course, '_id'), // One id to One Document
  ClassLoader: ... // see below One-To-Many Loader case
});

const server = new ApolloServer({
  typeDefs,
  resolvers,
  dataSources,
  ...
});

One-To-One Loader Case

The resolver to query for a list of Classes would be as follows:

// resolvers/Class.js
{
  Query: {
    getClasses: async (root, { courseId }, { dataSources }) => {
      // find all Classes with the given courseId
      const classes = await dataSources.Class.find({ courseId });
      return classes;
    },
  },
  Class: {
    course: async (root, _, { dataSources }) => {
      // returns a single doc for the given key
      const courseDoc = await dataSources.CourseLoader.load(root.course);
      return courseDoc;
    }
  }
}

Now the query will only make 2 calls to the DB 👏 Therefore avoiding duplicates documents as well.

One-To-Many Loader Case

However in my application I wanted to be able to run this query as well, without embedding a list of Class ids on the Course Document:

query {
  getCourses() {
    id
    name
    upcomingClasses {
      id
      dateOf
      course {
        id
        name
      }
    }
  }
}

Since there is no embedded list of ids to just feed to ClassLoader.loadMany() I had to adjust my batch function passed to Dataloader; hence the reason for me making this project as the other solutions only dealt with the OneToOneLoader situation. By using the createOneToManyMongooseLoader:

const dataSources = () => ({
  ...
  ClassLoader: createOneToManyMongooseLoader(Class, 'courseId') // One id to One Array of Documents.
});
// resolvers/Course.js

{
  Query: {
    getCourses: async (root, _, { dataSources }) => {
      return dataSources.Course.find();
    }
  },
  Course: {
    upcomingClasses: async (root, _, { dataSources }) => {
      // returns an array of Class docs that reference the courseId
      const classes = await dataSources.ClassLoader.load(root.id);
      // simulate filtering to only future classes
      return R.filter((class) => dateOf > new Date())(classes);
    }
  }
}

For Reference of the above example

Models

import mongoose, { Schema } from 'mongoose';

const ClassSchema = new Schema({
  id: String,
  date: String,
  courseId: String,
});

const ClassModel = mongoose.model('Class', ClassSchema);


const CourseSchema = new Schema({
  id: String,
  name: String,
});

const CourseModel = mongoose.model('Course', CourseSchema);

Mock Data

export const classes = [
  {
    id: '1',
    date: '20117-10-01',
    courseId: 'a',
  },
  {
    id: '2',
    date: '2017-01-01',
    courseId: 'a',
  },
  {
    id: '3',
    date: '2017-05-01',
    courseId: 'a',
  },
  {
    id: '4',
    date: '2019-08-01',
    courseId: 'b',
  },
  {
    id: '5',
    date: '2018-08-01',
    courseId: 'b',
  },
  {
    id: '6',
    date: '2018-10-01',
    courseId: 'c',
  },
];

export const courses = [
  {
    id: 'a',
    name: 'Course A',
  },
  {
    id: 'b',
    name: 'Course B',
  },
  {
    id: 'c',
    name: 'Course C',
  },
];

License MIT