Understanding Population in NestJS with MongoDB

nestjs population

When working with MongoDB and NestJS, relationships between collections are often necessary. MongoDB uses references (ObjectIds) to link documents, and Mongoose provides population to resolve these references. In this blog, we will explore different scenarios of population in NestJS with examples.

Setting Up NestJS with MongoDB

Before diving into population, let’s set up a NestJS project with MongoDB.

  1. Install NestJS CLI:

    npm i -g @nestjs/cli

  2. Create a new project:

    nest new nest-population-demo

  3. Install dependencies:

    npm install mongoose @nestjs/mongoose

Defining Schemas with Relationships

Let's define two schemas: User and Post, where a Post references a User.

import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
import { Types } from 'mongoose';

@Schema()
export class User extends Document {
 @Prop({ required: true })
 name: string;
}
export const UserSchema = SchemaFactory.createForClass(User);

@Schema()
export class Post extends Document {
 @Prop({ required: true })
 title: string;
 
 @Prop({ type: Types.ObjectId, ref: 'User' })
 author: User;
}
export const PostSchema = SchemaFactory.createForClass(Post);

Basic Population
Populating a Single Reference

To retrieve a post along with its author details:

import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Post } from './post.schema';

@Injectable()
export class PostService {
 constructor(@InjectModel(Post.name) private postModel: Model<Post>) {}

 async getPostWithAuthor(postId: string) {
   return this.postModel.findById(postId).populate('author').exec();
 }
}

Populating Multiple References

If a schema has multiple references, we can populate them all:

postModel.find().populate('author comments').exec();

Nested Population (Deep Population)

Sometimes, we need to populate nested relationships. For example, if a Post has Comments, and each Comment has a User, we can use deep population.

postModel.find().populate({
 path: 'comments',
 populate: {
   path: 'user',
   model: 'User',
 },
}).exec();

Selective Population

We can choose specific fields to populate:

postModel.find().populate('author', 'name').exec();

Conditional Population

Populate conditionally based on a query parameter:

postModel.find().populate({
 path: 'author',
 match: { role: 'admin' },
}).exec();

Virtual Population

Mongoose allows us to define virtual fields that don’t persist in the database but can be populated.

UserSchema.virtual('posts', {
 ref: 'Post',
 localField: '_id',
 foreignField: 'author',
});

Then populate like this:

userModel.find().populate('posts').exec();

Conclusion

Population in NestJS with MongoDB is powerful for handling document relationships. By using basic, nested, selective, conditional, and virtual population, we can efficiently manage and query relational data in a NoSQL environment.

Share this Post!