Before diving in, make sure you’ve read Part 1 of this series, where we explore what MongoDB is, its document-based structure, and why it's different from relational databases.
Introduction
In Part 1, we explored the foundations of MongoDB — a NoSQL, document-oriented database. Now in Part 2, we’ll take the next step by learning how to interact with MongoDB using Mongoose, a powerful ODM (Object Data Modeling) library for Node.js. We'll cover:
- Schema design in MongoDB
- Data types and relationships
- Setting up Mongoose
- CRUD operations
- Validations in Mongoose
- Key methods like
.save()
,.find()
,.findById()
, etc.
Let’s dive in! 🚀
1. Understanding Schema-less Nature of MongoDB
🔹 What is a Schema?
In relational databases like MySQL or PostgreSQL, a schema defines the structure of tables, columns, and their relationships. For example:
- Student
- Course
- Section
Each has a strict format and is connected through foreign keys. If you want to change the structure (add a new field), you need to alter the schema manually.
📌 This strict structure enforces consistency but reduces flexibility.
🔹 MongoDB is Schema-less
MongoDB doesn’t enforce any schema. Instead, it stores data in JSON-like documents, making it highly flexible and scalable.
For example, this is perfectly valid in MongoDB:
{ "name": "Ahmed Afzal", "age": 25 }
You can add more fields later without modifying the existing schema. However, this doesn't mean we should store data in a disorganized way. Best practice: define a logical document structure for consistency.
2. Data Types in MongoDB
Like all databases, MongoDB supports different data types:
Type | Example |
---|---|
String | { "name": "Ahmed" } |
Number |
{ "age": 25 } , { "price": 99.99 }
|
Boolean | { "isActive": true } |
Date | { "createdAt": ISODate("2025-07-07T00:00:00Z") } |
Array | { "skills": ["React", "Node.js"] } |
Object | { "address": { "city": "Karachi", "country": "Pakistan" } } |
Null | { "middleName": null } |
3. Embedded vs Referenced Documents
MongoDB allows you to structure related data in two ways:
✅ Embedded Documents (Denormalized)
Nested documents inside a parent document.
{
"title": "Intro to MongoDB",
"comments": [
{ "user": "Ali", "message": "Great post!" },
{ "user": "Sara", "message": "Helpful!" }
]
}
Benefits:
- Fast reads
- Fewer queries
Drawbacks:
- Documents can become large (max 16MB)
- Harder to update deeply nested data
🔗 Referenced Documents (Normalized)
Separate documents linked by IDs.
// Blog
{ "_id": 1, "title": "Intro to MongoDB", "comments": [101, 102] }
// Comments Collection
{ "_id": 101, "postId": 1, "message": "Nice Article" }
{ "_id": 102, "postId": 1, "message": "Great Concept" }
Benefits:
- More scalable
- Reusable data
Drawbacks:
- Slower reads due to multiple queries
4. Relationships in MongoDB
➤ One-to-One (1:1)
Each document relates to one other.
// User
{ "_id": 101, "name": "Ahmed" }
// Profile
{ "_id": 1, "userId": 101, "bio": "Engineer" }
➤ One-to-Many (1:N)
One document relates to many others.
// Author
{ "_id": 1, "name": "Ahmed" }
// Books
[
{ "_id": 101, "authorId": 1, "title": "Book A" },
{ "_id": 102, "authorId": 1, "title": "Book B" }
]
➤ Many-to-Many (M:N)
Both documents relate to many others.
// Student
{ "_id": 1, "name": "Ahmed", "enrolledCourses": [101, 102] }
// Courses
{ "_id": 101, "title": "React JS", "students": [1, 2] }
5. What is Mongoose and Why Use It?
Mongoose is an ODM library for Node.js that helps:
- Enforce structure (via schemas)
- Apply validations
- Easily perform CRUD operations
Without Mongoose, MongoDB allows unstructured data — e.g., inserting a string into a number field would still work. Mongoose prevents that.
6. Setting Up Mongoose in Node.js
✅ Prerequisites
- Install Node.js
- Ensure MongoDB is running locally (
mongod
) - Optionally use MongoDB Compass
📁 Folder Structure
mongoose-app/
├── models/
│ └── User.js
├── app.js
📦 Initialize Project
mkdir mongoose-app
cd mongoose-app
npm init -y
npm install mongoose
🔌 Connect to MongoDB
// app.js
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/mydb', {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => console.log("Connected to MongoDB"))
.catch((err) => console.error("Connection error:", err));
7. Creating Schemas and Models
// models/User.js
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
name: String,
age: Number,
});
const User = mongoose.model('User', userSchema);
module.exports = User;
8. CRUD Operations in Mongoose
📌 Create
const ahmed = new User({ name: "Ahmed", age: 25 });
ahmed.save();
📌 Read
User.find().then(users => console.log(users));
📌 Update
User.updateOne({ name: "Ahmed" }, { age: 30 });
📌 Delete
User.deleteOne({ name: "Ahmed" });
✅ Full Example (app.js
)
const mongoose = require('mongoose');
const User = require('./models/User');
mongoose.connect('mongodb://localhost:27017/mydb', {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(async () => {
console.log("Connected to MongoDB");
// Create
const user = new User({ name: "Ahmed", age: 22 });
await user.save();
// Read
const users = await User.find();
console.log(users);
// Update
await User.updateOne({ name: "Ahmed" }, { age: 30 });
// Delete
await User.deleteOne({ name: "Ahmed" });
mongoose.disconnect();
})
.catch(console.error);
9. Mongoose Validations
Without validation:
const user = new User({ name: 123, age: "abc" }); // Allowed!
With validation:
const userSchema = new mongoose.Schema({
name: { type: String, required: true, minlength: 3 },
age: { type: Number, required: true, min: 18, max: 60 },
email: { type: String, required: true, match: /.+\@.+\..+/ }
});
✨ Example:
const user = new User({
name: "Ah", // too short
age: 17, // too young
email: "invalidemail"
});
user.save().catch(err => console.log("Validation Error:", err.message));
🔧 Other Validation Options
Validator | Description |
---|---|
required |
Must be present |
min / max
|
Numbers only |
minlength / maxlength
|
String length |
match |
Regex pattern |
enum |
Limited values |
status: {
type: String,
enum: ['active', 'inactive', 'pending'],
default: 'pending'
}
10. Mongoose Query Methods Explained
Method | Use |
---|---|
.save() |
Save new document |
.find() |
Get all or filtered documents |
.findOne() |
Get one document |
.findById() |
Get by ObjectId |
.updateOne() |
Update first match |
.findByIdAndUpdate() |
Update by ID and return updated doc |
.deleteOne() |
Delete first match |
.findByIdAndDelete() |
Delete by ID |
Conclusion
In this part, you learned how to:
- Understand MongoDB’s flexible data model
- Use embedded and referenced documents
- Create and validate schemas using Mongoose
- Perform full CRUD operations
🔁 If you haven't already, read Part 1 here to better understand MongoDB fundamentals.
👉 Coming Up Next (Part 3 – Final Part):
In the next article, we’ll explore:
- Relationships using
populate()
- Advanced queries and filtering
- Indexing for performance
- Aggregation framework
- Project structure best practices
Stay tuned! 🎯
Top comments (0)