Mongoose for Beginners: A Friendly Introduction
Table of contents
- What is Mongoose?
- Why Use Mongoose?
- Step 1: Setting Up Mongoose
- Step 2: Connecting to MongoDB
- Step 3: Defining a Schema
- 1. Mongoose Core Functions
- 2. Schema Functions
- 3. Model Functions
- 3.1. mongoose.model()
- 3.2. Model.create()
- 3.3. Model.find()
- 3.4. Model.findOne()
- 3.5. Model.findById()
- 3.6. Model.findOneAndUpdate()
- 3.7. Model.findByIdAndUpdate()
- 3.8. Model.findOneAndDelete()
- 3.9. Model.findByIdAndDelete()
- 3.10. Model.updateOne()
- 3.11. Model.updateMany()
- 3.12. Model.deleteOne()
- 3.13. Model.deleteMany()
- 3.14. Model.countDocuments()
- 3.15. Model.estimatedDocumentCount()
- 3.16. Model.createCollection()
- 4. Document Functions
- 5. Query Functions
- 6. Middleware Functions
- 7. Utility Functions
- Conclusion
If you're starting with Node.js and MongoDB, you've likely come across Mongoose. It’s a powerful library that simplifies working with MongoDB in a Node.js environment. Mongoose provides an easy-to-use API to model and interact with data while adding a layer of abstraction to MongoDB's native methods.
In this blog post, we’ll explore Mongoose from a beginner's perspective. We’ll cover its basic concepts, setting it up, and how to use it effectively in your Node.js applications.
What is Mongoose?
Mongoose is an Object Data Modeling (ODM) library for MongoDB and Node.js. In simpler terms, it allows you to define schemas and models for MongoDB collections and provides a straightforward way to interact with those collections.
Schema: Defines the structure of your documents in a MongoDB collection. Think of it as a blueprint for your data.
Model: A constructor compiled from a Schema that allows you to create and query documents in a collection.
Why Use Mongoose?
Schema Validation: It ensures that the data saved to your MongoDB database matches the defined structure.
Data Modeling: Mongoose allows you to define relationships between data, making it easier to work with complex documents.
Built-in Query Methods: Mongoose provides helpful methods to find, update, delete, and manipulate data in a MongoDB database.
Middleware: Mongoose supports hooks (middleware) that run before or after certain actions like saving or updating a document.
Step 1: Setting Up Mongoose
Before using Mongoose, ensure you have Node.js and MongoDB installed.
Create a New Node.js Project:
Open your terminal and run the following commands:
mkdir my-mongoose-project cd my-mongoose-project npm init -y
Install Mongoose:
Now, install Mongoose using npm:
npm install mongoose
Step 2: Connecting to MongoDB
To interact with MongoDB using Mongoose, you first need to establish a connection. Here’s how you can do that:
we used the mongoose.connect()
method to establish a connection to MongoDB. This method accepts two parameters:
The connection string (URL): This is the address to your MongoDB instance. If you're using MongoDB Atlas (a cloud-hosted version of MongoDB), you’ll get a connection string directly from your Atlas dashboard.
Options (object): This is an optional object that contains various configuration settings that help control how the connection is made.
Let’s break it down and explain how to structure the connection for both local MongoDB and MongoDB Atlas.
1. MongoDB Atlas Connection URL
When you're using MongoDB Atlas, you get a connection string that looks like this:
mongodb+srv://<username>:<password>@cluster0.mongodb.net/<dbname>?retryWrites=true&w=majority
You’ll need to replace:
<username>
: Your MongoDB Atlas username<password>
: Your MongoDB Atlas password<dbname>
: The database name you want to connect to
2. Connection Options (Object)
The connection options object is passed as the second parameter to mongoose.connect()
. Some commonly used options are:
useNewUrlParser: true
: This option tells Mongoose to use the new MongoDB connection string parser (this is required because MongoDB has updated how connection strings are handled).useUnifiedTopology: true
: This enables the new unified topology layer, which improves server monitoring and load balancing.retryWrites: true
: Allows automatic retries for write operations in case of transient network errors.
Create a file called app.js
and add the following code to connect to MongoDB:
javascriptCopyconst mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/mydatabase', { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => console.log('Connected to MongoDB'))
.catch(err => console.error('Could not connect to MongoDB...', err));
In this example, mongodb://localhost:27017/mydatabase
is the connection string for MongoDB running locally. If you're using a remote MongoDB service (like MongoDB Atlas), replace this URL with the appropriate one.
Other Best Option :-
It's a good practice to separate concerns in your code. Instead of placing the MongoDB connection code directly in your app.js
, you can create a separate connection file to handle the connection. This makes the code cleaner, more organized, and easier to maintain.
Let’s create a db.js
file to manage the database connection.
Create a
db.js
File:In your project folder, create a file named
db.js
:javascriptCopyconst mongoose = require('mongoose'); // Function to connect to MongoDB function connectDB() { mongoose.connect('mongodb://localhost:27017/mydatabase', { useNewUrlParser: true, useUnifiedTopology: true }) .then(() => console.log('Connected to MongoDB')) .catch(err => console.error('Could not connect to MongoDB...', err)); } // Export the connection function module.exports = connectDB;
Import and Use the Connection in
app.js
:Now, in your
app.js
, you can simply import theconnectDB
function and call it to establish the connection to MongoDB.javascriptCopyconst connectDB = require('./db'); // Import the connection function const User = require('./models/User'); // Import the User model connectDB(); // Call the connection function async function createUser() { const user = new User({ name: 'John Doe', email: 'john@example.com', age: 30 }); try { const result = await user.save(); console.log('User saved:', result); } catch (err) { console.error('Error saving user:', err); } } createUser();
This way, your app.js
remains focused on the application logic, while the database connection logic is handled separately in db.js
. This approach makes your codebase cleaner and more maintainable, especially as your project grows.
Step 3: Defining a Schema
Schemas in Mongoose define the structure of the documents (Simple means the structure of Table like in SQL here its called as document ) you’ll store in your MongoDB collections. Let's define a simple User
schema with a name, email, and age.
Create a file called models/User.js
and add this code:
javascriptCopyconst mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
age: {
type: Number,
required: true
}
});
// Create a model based on the schema
const User = mongoose.model('User', userSchema);
module.exports = User;
In this schema:
name
,email
, andage
are the fields of the document.required: true
ensures these fields must have values when creating a document.unique: true
ensures that the email field is unique.
Mongoose provides a range of built-in validation features to ensure that the data you store in your MongoDB database meets certain criteria. These validations can be applied at the schema level, which means they are automatically enforced when you create or update documents.
Here are the main types of validations provided by Mongoose:
Required: Ensures a field is not empty.
Type: Ensures the field matches a specific type (e.g., String, Number).
String Length: Ensures strings meet minimum and/or maximum length.
Min/Max: Ensures numeric fields are within a specified range.
Enum: Ensures a field's value is one of the defined options.
Custom: Adds custom logic for validation.
Match: Ensures strings match a regular expression pattern.
Unique: Ensures values are unique across documents (requires a unique index).
Async: Allows asynchronous validation (e.g., checking for existing data).
Custom Error Messages: Customizable messages for all validation types.
These validations help ensure that the data you save into your MongoDB database is consistent and valid, reducing the likelihood of errors and data integrity issues in your application.
**Not Important for beginners** Mongoose's built-in validation system is powerful and covers most common use cases, but there are situations where you might want to use an external library to handle more complex validation logic.
Joi is excellent for more complex schema-based validation and integrates well with Mongoose.
Validator is a simple, lightweight solution for common validations like email, URL, etc.
Mongoose-Validator extends Mongoose’s built-in validation capabilities with some additional validators.
Celebrate works well with Express.js for structured validation of HTTP request data.
Step 4: Creating and Saving Documents
Now that we have our schema and model, let’s create and save a new User
document in the database. In your app.js
file, you can add the following code:
We have user model we can use this model reference to execute queries and CRUD Operations.
javascriptCopyconst mongoose = require('mongoose');
const User = require('./models/User'); // Import the User model
mongoose.connect('mongodb://localhost:27017/mydatabase', { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => console.log('Connected to MongoDB'))
.catch(err => console.error('Could not connect to MongoDB...', err));
async function createUser() {
const user = new User({
name: 'John Doe',
email: 'john@example.com',
age: 30
});
try {
const result = await user.save();
console.log('User saved:', result);
} catch (err) {
console.error('Error saving user:', err);
}
}
createUser();
This code does the following:
Creates a new
User
object using theUser
model.Saves the user to the MongoDB database with
user.save()
.
Step 5: Querying Data ( CRUD Operations )
One of the powerful features of Mongoose is its ability to query documents in a MongoDB collection using built-in methods. Let’s query all users from the database:
javascriptCopyasync function getUsers() {
try {
const users = await User.find(); // Finds all users
console.log('Users:', users);
} catch (err) {
console.error('Error fetching users:', err);
}
}
getUsers();
To query a specific user by their email:
javascriptCopyasync function getUserByEmail(email) {
try {
const user = await User.findOne({ email: email });
if (!user) {
console.log('User not found');
} else {
console.log('User found:', user);
}
} catch (err) {
console.error('Error fetching user:', err);
}
}
getUserByEmail('john@example.com');
Step 6: Updating Data
You can also update existing documents with Mongoose. Here’s an example that updates the age of a user:
javascriptCopyasync function updateUserEmail(oldEmail, newEmail) {
try {
const user = await User.findOneAndUpdate(
{ email: oldEmail },
{ email: newEmail },
{ new: true } // Return the updated document
);
console.log('Updated user:', user);
} catch (err) {
console.error('Error updating user:', err);
}
}
updateUserEmail('john@example.com', 'john.doe@example.com');
Step 7: Deleting Data
Finally, deleting data is just as easy:
javascriptCopyasync function deleteUserByEmail(email) {
try {
const result = await User.deleteOne({ email: email });
if (result.deletedCount === 0) {
console.log('No user found with that email');
} else {
console.log('User deleted');
}
} catch (err) {
console.error('Error deleting user:', err);
}
}
deleteUserByEmail('john.doe@example.com');
Comprehensive List of Functions Provided by Mongoose
Mongoose is a powerful Object Data Modeling (ODM) library for MongoDB and Node.js. It provides a range of methods for working with data in MongoDB, making it easier to define, query, update, and validate documents. Below is a list of all the major functions and methods provided by Mongoose, divided into categories for easier reference.
1. Mongoose Core Functions
These are some of the key functions available from Mongoose itself.
1.1. mongoose.connect()
Establishes a connection to a MongoDB database.
javascriptCopymongoose.connect('mongodb://localhost:27017/mydatabase', {
useNewUrlParser: true,
useUnifiedTopology: true,
});
1.2. mongoose.disconnect()
Disconnects from the MongoDB database.
javascriptCopymongoose.disconnect();
1.3. mongoose.connection
Accesses the Mongoose connection instance (for monitoring or managing the connection).
javascriptCopyconst connection = mongoose.connection;
2. Schema Functions
Schemas define the structure of documents within a collection. Mongoose provides various methods to define and manipulate schemas.
2.1. new mongoose.Schema()
Creates a new schema instance that defines the structure of the documents.
javascriptCopyconst userSchema = new mongoose.Schema({
name: String,
email: { type: String, required: true },
});
2.2. schema.add()
Adds additional fields to a schema.
javascriptCopyuserSchema.add({ age: Number });
2.3. schema.path()
Accesses a particular path within a schema.
javascriptCopyconst path = userSchema.path('email');
2.4. schema.pre()
and schema.post
()
Adds middleware functions to be executed before or after certain events like save, validate, etc.
javascriptCopyuserSchema.pre('save', function(next) {
// Do something before saving
next();
});
3. Model Functions
Models are constructors compiled from schemas. These are used to create and interact with documents in MongoDB.
3.1. mongoose.model()
Compiles a model from a schema.
javascriptCopyconst User = mongoose.model('User', userSchema);
3.2. Model.create()
Creates one or more documents and saves them to the database.
javascriptCopyUser.create({ name: 'Alice', email: 'alice@example.com' });
3.3. Model.find()
Finds documents in the collection that match the query.
javascriptCopyUser.find({ name: 'Alice' }, (err, users) => { /* handle result */ });
3.4. Model.findOne()
Finds a single document based on a query.
javascriptCopyUser.findOne({ email: 'alice@example.com' }, (err, user) => { /* handle result */ });
3.5. Model.findById()
Finds a document by its _id
.
javascriptCopyUser.findById('60b7f27a2f3f2b356c1c5eab', (err, user) => { /* handle result */ });
3.6. Model.findOneAndUpdate()
Finds a document and updates it.
javascriptCopyUser.findOneAndUpdate({ email: 'alice@example.com' }, { age: 30 });
3.7. Model.findByIdAndUpdate()
Finds a document by its _id
and updates it.
javascriptCopyUser.findByIdAndUpdate('60b7f27a2f3f2b356c1c5eab', { age: 30 });
3.8. Model.findOneAndDelete()
Finds a document and deletes it.
javascriptCopyUser.findOneAndDelete({ email: 'alice@example.com' });
3.9. Model.findByIdAndDelete()
Finds a document by its _id
and deletes it.
javascriptCopyUser.findByIdAndDelete('60b7f27a2f3f2b356c1c5eab');
3.10. Model.updateOne()
Updates a single document that matches the query.
javascriptCopyUser.updateOne({ email: 'alice@example.com' }, { age: 30 });
3.11. Model.updateMany()
Updates all documents that match the query.
javascriptCopyUser.updateMany({ age: { $lt: 18 } }, { adult: false });
3.12. Model.deleteOne()
Deletes a single document based on a query.
javascriptCopyUser.deleteOne({ email: 'alice@example.com' });
3.13. Model.deleteMany()
Deletes all documents that match the query.
javascriptCopyUser.deleteMany({ age: { $lt: 18 } });
3.14. Model.countDocuments()
Counts the number of documents that match a query.
javascriptCopyUser.countDocuments({ age: { $lt: 18 } });
3.15. Model.estimatedDocumentCount()
Estimates the number of documents in a collection.
javascriptCopyUser.estimatedDocumentCount();
3.16. Model.createCollection()
Creates the collection in the database (if it doesn’t exist).
javascriptCopyUser.createCollection();
4. Document Functions
Mongoose documents represent instances of your models. These functions are available to the documents themselves.
4.1. document.save
()
Saves a document to the database.
javascriptCopyconst user = new User({ name: 'Alice', email: 'alice@example.com' });
user.save();
4.2. document.updateOne()
Updates a document.
javascriptCopyuser.updateOne({ age: 30 });
4.3. document.remove()
Removes a document from the database.
javascriptCopyuser.remove();
4.4. document.validate()
Validates a document based on its schema.
javascriptCopyuser.validate((err) => { /* handle validation result */ });
5. Query Functions
Mongoose provides a variety of functions to help build and execute queries. These functions are chainable and allow you to filter, sort, limit, and manipulate the documents returned from the database.
5.1. Query.where()
Adds a condition to a query.
javascriptCopyUser.where('name').equals('Alice');
5.2. Query.find()
Finds documents that match the query.
javascriptCopyUser.find({ age: { $gt: 18 } });
5.3. Query.limit()
Limits the number of documents returned by the query.
javascriptCopyUser.find().limit(5);
5.4. Query.sort()
Sorts the query results.
javascriptCopyUser.find().sort({ age: -1 });
5.5. Query.select
()
Selects specific fields to be returned in the result.
javascriptCopyUser.find().select('name email');
5.6. Query.skip()
Skips a specified number of documents in the query result.
javascriptCopyUser.find().skip(10);
5.7. Query.populate()
Populates referenced documents (using MongoDB's ObjectId
references).
javascriptCopyUser.find().populate('posts');
6. Middleware Functions
Middleware functions in Mongoose allow you to run code before or after certain events, such as saving or updating documents.
6.1. schema.pre()
Adds a pre-save middleware to execute before an action (like save, remove, etc.).
javascriptCopyuserSchema.pre('save', function(next) {
// Perform validation or other logic
next();
});
6.2. schema.post
()
Adds a post-save middleware to execute after an action.
javascriptCopyuserSchema.post('save', function(doc) {
// Perform actions after document is saved
});
7. Utility Functions
7.1. mongoose.Types.ObjectId()
Creates a new MongoDB ObjectId.
javascriptCopyconst id = mongoose.Types.ObjectId();
7.2. mongoose.Schema.Types
Provides custom types for schemas, such as ObjectId
, Buffer
, Decimal128
, etc.
javascriptCopyconst userSchema = new mongoose.Schema({
userId: mongoose.Schema.Types.ObjectId,
});
If you like my blog Do connect me on linkdIN
Conclusion
Mongoose is a fantastic tool for working with MongoDB in Node.js applications. It simplifies the process of interacting with your database by providing a more intuitive and object-oriented way to model your data. With Mongoose, you can easily define schemas, validate data, and perform CRUD operations while benefiting from a variety of built-in features.
We’ve just scratched the surface of what Mongoose can do. If you’re building a Node.js application that uses MongoDB, Mongoose is an excellent tool to streamline development and help you write cleaner, more maintainable code.
If you have any questions or need further clarification on any concepts, feel free to ask me on LinkedIn!
If you enjoyed this blog and would like to stay connected, feel free to connect with me on LinkedIn! I’m always happy to engage with fellow developers, share knowledge, and discuss ideas. Let’s connect and grow together in the world of tech!
Happy coding! 🚀
Useful Links:
Mongoose Documentation