Search code examples
javascriptmongodbmongooseaggregation-frameworkapollo-server

parent argument in Apollo GraphQL resolver vs $lookup stage in MongoDB aggregation for joining Mongoose models


I'm developing a backend application using Apollo GraphQL and Mongoose. I have two models: User and Post, which have a one-to-many relationship (a user can have many posts, but a post belongs to one user).

I want to query the user field in my GraphQL schema to fetch a user by its id, along with all the posts that belong to that user. I also want to query the author field in my Post type to fetch the user who wrote that post.

I have learned that there are two ways to join data from different collections in MongoDB: using the parent argument in Apollo GraphQL resolver, or using the $lookup stage in MongoDB aggregation.

The parent argument is an object that contains the result returned from the resolver on the parent field. The parent argument can be used to access data from the parent field or type, and pass it to the child field or type. For example, I can use it to join mongoose models like this:

type User {
  id: ID!
  name: String
  posts: [Post]
}

type Post {
  id: ID!
  title: String
  author: User
}

type Query {
  user(id: ID!): User
}

const resolvers = {
  User: {
    posts(parent, args, context) {
      return context.db.Post.find({ author: parent.id });
    },
  },
};

The $lookup stage is an aggregation pipeline stage that performs a left outer join to another collection in the same database. The $lookup stage can be used to combine data from multiple collections into a single result set. For example, I can use it to join models like this:

db.posts
  .aggregate([
    {
      $lookup: {
        from: "users",
        localField: "author",
        foreignField: "_id",
        as: "author",
      },
    },
  ])
  .toArray((err, posts) => {
    console.log(posts);
  });

I'm not sure which one to use for my application. I want to choose the option that is more efficient, scalable, and maintainable.

What are the key differences and trade-offs between using the parent argument in Apollo GraphQL resolver and using the $lookup stage in MongoDB aggregation for joining Mongoose models?


Solution

  • There is indeed some differences to know between the two methods

    Using the resolvers

    I find this approach more intuitive and readable, and so easier to maintain, especially for complex queries

    However, it might not be the most efficient when dealing with a large amount of data, as it makes extra calls to the database for each nested field. This could be a performance bottleneck when dealing with a high volume of complex nested queries.

    Using $lookup

    On the other hand, $lookup operations are generally more performant, as they uses a single database call to join and retrieve data.

    However, the $lookup operation can become complex and harder to read and maintain as your queries grow in complexity. It may require more knowledge about MongoDB's query language and may not be as straightforward to use as the resolvers method. Poorly written aggregation can lead to performance issue, so be absolutely certain about what you are doing.

    So in your example i wouldn't mind using the resolver method, even if using aggregate is faster, the first method would be much easier to read / improve / maintain.

    Hope that's help :)