
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.
-
Install NestJS CLI:
npm i -g @nestjs/cli
-
Create a new project:
nest new nest-population-demo
-
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.