DEV Community

Harsh Mishra
Harsh Mishra

Posted on

1

Understanding Relationships in MongoDB and Mongoose

Understanding Relationships in MongoDB & Mongoose

Introduction

MongoDB, being a NoSQL database, handles relationships differently than relational databases like MySQL. Instead of foreign keys, it uses Embedded Documents (Denormalization) and References (Normalization) to establish connections between data.

In this guide, we’ll explore:

  • One-to-One (1:1) Relationships
  • One-to-Many (1:M) Relationships
  • Many-to-Many (M:N) Relationships
  • How to define them in MongoDB and Mongoose
  • How to query them and what the results look like

Let’s dive in! 🚀

1. Types of Relationships in MongoDB

In MongoDB, relationships can be represented in two ways:

  1. Embedded Documents (Denormalization)
  2. References (Normalization)

Just like MySQL, MongoDB supports:

  • One-to-One (1:1)
  • One-to-Many (1:M)
  • Many-to-Many (M:N)

For each type, I’ll cover:

  • MongoDB Schema (Embedded & Referenced)
  • Querying in MongoDB
  • How to Define it in Mongoose
  • Querying with Mongoose
  • Example Output

2. One-to-One (1:1) Relationship

Example Scenario

A User has one Profile, and a Profile belongs to only one User.


Method 1: Using Embedded Document (Denormalization)

MongoDB Schema

{
  "_id": ObjectId("user_id"),
  "name": "John",
  "profile": {
    "bio": "Loves coding"
  }
}
Enter fullscreen mode Exit fullscreen mode

Mongoose Schema

const userSchema = new mongoose.Schema({
  name: String,
  profile: {
    bio: String
  }
});
const User = mongoose.model("User", userSchema);
Enter fullscreen mode Exit fullscreen mode

Querying in Mongoose

const user = await User.findOne();
console.log(user);
Enter fullscreen mode Exit fullscreen mode

Example Output

{
  "_id": "user_id",
  "name": "John",
  "profile": {
    "bio": "Loves coding"
  }
}
Enter fullscreen mode Exit fullscreen mode

Method 2: Using References (Normalization)

MongoDB Schema (Separate Documents)

{
  "_id": ObjectId("user_id"),
  "name": "John",
  "profile": ObjectId("profile_id")
}
Enter fullscreen mode Exit fullscreen mode
{
  "_id": ObjectId("profile_id"),
  "bio": "Loves coding",
  "user": ObjectId("user_id")
}
Enter fullscreen mode Exit fullscreen mode

Mongoose Schema

const profileSchema = new mongoose.Schema({
  bio: String,
  user: { type: mongoose.Schema.Types.ObjectId, ref: "User" }
});

const userSchema = new mongoose.Schema({
  name: String,
  profile: { type: mongoose.Schema.Types.ObjectId, ref: "Profile" }
});

const Profile = mongoose.model("Profile", profileSchema);
const User = mongoose.model("User", userSchema);
Enter fullscreen mode Exit fullscreen mode

Querying in Mongoose (Using .populate())

const user = await User.findOne().populate("profile");
console.log(user);
Enter fullscreen mode Exit fullscreen mode

Example Output

{
  "_id": "user_id",
  "name": "John",
  "profile": {
    "_id": "profile_id",
    "bio": "Loves coding"
  }
}
Enter fullscreen mode Exit fullscreen mode

3. One-to-Many (1:M) Relationship

Example Scenario

A User has many Posts, but each Post belongs to only one User.


Method 1: Using Embedded Documents (Denormalization)

MongoDB Schema

{
  "_id": ObjectId("user_id"),
  "name": "John",
  "posts": [
    { "title": "MongoDB Guide", "content": "MongoDB is great!" },
    { "title": "Mongoose Intro", "content": "Mongoose is helpful!" }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Mongoose Schema

const userSchema = new mongoose.Schema({
  name: String,
  posts: [
    {
      title: String,
      content: String
    }
  ]
});
const User = mongoose.model("User", userSchema);
Enter fullscreen mode Exit fullscreen mode

Querying in Mongoose

const user = await User.findOne();
console.log(user);
Enter fullscreen mode Exit fullscreen mode

Example Output

{
  "_id": "user_id",
  "name": "John",
  "posts": [
    { "title": "MongoDB Guide", "content": "MongoDB is great!" },
    { "title": "Mongoose Intro", "content": "Mongoose is helpful!" }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Method 2: Using References (Normalization)

MongoDB Schema

{
  "_id": ObjectId("user_id"),
  "name": "John",
  "posts": [ObjectId("post1_id"), ObjectId("post2_id")]
}
Enter fullscreen mode Exit fullscreen mode
{
  "_id": ObjectId("post1_id"),
  "title": "MongoDB Guide",
  "content": "MongoDB is great!",
  "user": ObjectId("user_id")
}
Enter fullscreen mode Exit fullscreen mode

Mongoose Schema

const postSchema = new mongoose.Schema({
  title: String,
  content: String,
  user: { type: mongoose.Schema.Types.ObjectId, ref: "User" }
});

const userSchema = new mongoose.Schema({
  name: String,
  posts: [{ type: mongoose.Schema.Types.ObjectId, ref: "Post" }]
});

const Post = mongoose.model("Post", postSchema);
const User = mongoose.model("User", userSchema);
Enter fullscreen mode Exit fullscreen mode

Querying in Mongoose

const user = await User.findOne().populate("posts");
console.log(user);
Enter fullscreen mode Exit fullscreen mode

Example Output

{
  "_id": "user_id",
  "name": "John",
  "posts": [
    { "_id": "post1_id", "title": "MongoDB Guide", "content": "MongoDB is great!" },
    { "_id": "post2_id", "title": "Mongoose Intro", "content": "Mongoose is helpful!" }
  ]
}
Enter fullscreen mode Exit fullscreen mode

4. Many-to-Many (M:N) Relationship

Example Scenario

A Student can enroll in many Courses, and a Course can have many Students.


Using References with a Junction Collection

MongoDB Schema

{
  "_id": ObjectId("student_id"),
  "name": "John",
  "courses": [ObjectId("course1_id"), ObjectId("course2_id")]
}
Enter fullscreen mode Exit fullscreen mode
{
  "_id": ObjectId("course1_id"),
  "title": "Math 101",
  "students": [ObjectId("student_id")]
}
Enter fullscreen mode Exit fullscreen mode

Mongoose Schema

const studentSchema = new mongoose.Schema({
  name: String,
  courses: [{ type: mongoose.Schema.Types.ObjectId, ref: "Course" }]
});

const courseSchema = new mongoose.Schema({
  title: String,
  students: [{ type: mongoose.Schema.Types.ObjectId, ref: "Student" }]
});

const Student = mongoose.model("Student", studentSchema);
const Course = mongoose.model("Course", courseSchema);
Enter fullscreen mode Exit fullscreen mode

Querying in Mongoose

const student = await Student.findOne().populate("courses");
console.log(student);
Enter fullscreen mode Exit fullscreen mode

Example Output

{
  "name": "John",
  "courses": [
    { "_id": "course1_id", "title": "Math 101" },
    { "_id": "course2_id", "title": "Physics 201" }
  ]
}
Enter fullscreen mode Exit fullscreen mode

5. Summary Table

Relationship Embedded Documents References with .populate()
One-to-One { profile: { bio: "text" } } { profile: ObjectId("profile_id") }
One-to-Many { posts: [ {title, content} ] } { posts: [ObjectId("post_id")] }
Many-to-Many ❌ (not ideal) { courses: [ObjectId("course_id")] }

Conclusion

  • Embedded documents are good for fast reads but increase duplication.
  • References with .populate() keep data normalized but require joins.
  • Mongoose makes relationships easy using Schema.Types.ObjectId.

Image of Datadog

How to Diagram Your Cloud Architecture

Cloud architecture diagrams provide critical visibility into the resources in your environment and how they’re connected. In our latest eBook, AWS Solution Architects Jason Mimick and James Wenzel walk through best practices on how to build effective and professional diagrams.

Download the Free eBook

Top comments (0)

Image of DataStax

AI Agents Made Easy with Langflow

Connect models, vector stores, memory and other AI building blocks with the click of a button to build and deploy AI-powered agents.

Get started for free

👋 Kindness is contagious

Engage with a wealth of insights in this thoughtful article, valued within the supportive DEV Community. Coders of every background are welcome to join in and add to our collective wisdom.

A sincere "thank you" often brightens someone’s day. Share your gratitude in the comments below!

On DEV, the act of sharing knowledge eases our journey and fortifies our community ties. Found value in this? A quick thank you to the author can make a significant impact.

Okay