Share
Explore

Projected JSON collections and the Front end and back end communication via RESTful apis

The code to list out students with their associated classes and list out class with its associated students

Here’s the Node.js (Express + Mongoose) code to list:
Students with their associated classes
Classes with their associated students

1️⃣ Get Students with Their Associated Classes

Student model has an enrollments field that references Enrollment, and Enrollment references Class.
app.get('/api/students-with-classes', async (req, res) => {
try {
const students = await Student.find({})
.populate({
path: 'enrollments',
populate: { path: 'class' } // Populate the class inside enrollments
});

res.json(students);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
path: 'enrollments' replaces the enrollments field with full enrollment details.
path: 'class' replaces the class field with full class details.

Explanation:

Finds all students.
Populates their enrollments.
Further populates the class field within enrollments.
What does path: reference?
In Mongoose's .populate() method, path refers to the field in the schema that contains a reference to another model (i.e., a field that stores an ObjectId linking to another collection).

Example 1: Single-Level Population

Consider these schemas:
const mongoose = require('mongoose');

const StudentSchema = new mongoose.Schema({
name: String,
enrollments: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Enrollment' }]
});

const ClassSchema = new mongoose.Schema({
name: String,
enrollments: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Enrollment' }]
});

const EnrollmentSchema = new mongoose.Schema({
student: { type: mongoose.Schema.Types.ObjectId, ref: 'Student' },
class: { type: mongoose.Schema.Types.ObjectId, ref: 'Class' }
});

const Student = mongoose.model('Student', StudentSchema);
const Class = mongoose.model('Class', ClassSchema);
const Enrollment = mongoose.model('Enrollment', EnrollmentSchema);

Getting the students and the associated enrollments

const students = await Student.find().populate('enrollments');

Example 2: Nested Population

If you want to retrieve students along with their classes, you need nested population:
const students = await Student.find()
.populate({
path: 'enrollments', // First, populate enrollments
populate: { path: 'class' } // Then, populate the class field inside enrollments
});
First path: 'enrollments' → Populates the enrollments field in Student, replacing the ObjectIds with actual Enrollment documents.
Second populate: { path: 'class' } → Looks inside each populated Enrollment and populates the class field.

Example 3: Populating from Enrollment

If we query enrollments and want both the student and class:
const enrollments = await Enrollment.find()
.populate('student') // Populates student field
.populate('class'); // Populates class field

Summary

path specifies which field (that contains an ObjectId) to populate.
You can nest populate to populate fields inside populated documents.
.populate() is used to replace ObjectIds with actual referenced documents.

How path works with .populate() in a Student - Enrollment - Class relationship:

🖼️ How .populate() Works

image.png

🖥️ Mongoose Queries & .populate() Usage

1️⃣ Getting Students with Their Classes

const students = await Student.find()
.populate({
path: 'enrollments', // Populate enrollments field in Student
populate: { path: 'class' } // Further populate class inside enrollments
});

💡 Result:
[
{
"_id": "101",
"name": "John",
"enrollments": [
{ "class": { "_id": "301", "name": "Math" } }
]
},
{
"_id": "102",
"name": "Alice",
"enrollments": [
{ "class": { "_id": "301", "name": "Math" } }
]
}
]

2️⃣ Getting Classes with Their Students

const classes = await Class.find()
.populate({
path: 'enrollments', // Populate enrollments field in Class
populate: { path: 'student' } // Further populate student inside enrollments
});

💡 Result:
[
{
"_id": "301",
"name": "Math",
"enrollments": [
{ "student": { "_id": "101", "name": "John" } },
{ "student": { "_id": "102", "name": "Alice" } }
]
}
]

🔑 Key Takeaways

path: 'enrollments' → Fetches enrollments linked to students/classes. ✔ populate: { path: 'class' } → Further fetches class inside each enrollment. ✔ populate: { path: 'student' } → Fetches student details for each class. ✔ Nested .populate() allows deep relationships!


Visualize how .populate() works in a Student - Enrollment - Class relationship.


Students Collection Enrollments Collection Classes Collection
┌──────────────────────┐ ┌────────────────────────────┐ ┌──────────────────────┐
| _id: 101 | | _id: 201 | | _id: 301 |
| name: "John" | ───────> | student: 101 | ───────> | name: "Math" |
| enrollments: [201] | | class: 301 | └──────────────────────┘
├──────────────────────┤ ├────────────────────────────┤
| _id: 102 | | _id: 202 |
| name: "Alice" | ───────> | student: 102 | ───────> | _id: 301 |
| enrollments: [202] | | class: 301 | | name: "Math" |
└──────────────────────┘ └────────────────────────────┘ └──────────────────────┘

🔄 How .populate() Works in Code

1️⃣ Getting Students with Their Classes

const students = await Student.find()
.populate({
path: 'enrollments',
populate: { path: 'class' }
Want to print your doc?
This is not the way.
Try clicking the ⋯ next to your doc name or using a keyboard shortcut (
CtrlP
) instead.