Mongoose: Simplifying MongoDB For Node.js
Published: 2024-07-12 10:24:20


by [Enemuoh Everist](https://blog.openreplay.com/authors/enemuoh-everist) 




<blockquote><em>
Within the domain of Node.js and MongoDB, Mongoose plays a crucial role as one of the most widely embraced libraries. It serves as an interface between JavaScript applications and MongoDB. This article will explore Mongoose in depth.
</em></blockquote>



<div style="background-color:#efefef; border-radius:8px; padding:10px; display:block;">
<hr/>
<h3><em>Session Replay for Developers</em></h3> 
<p><em>Uncover frustrations, understand bugs and fix slowdowns like never before with <strong><a href="https://github.com/openreplay/openreplay" target="_blank">OpenReplay</a></strong> — an open-source session replay suite for developers. It can be <strong>self-hosted</strong> in minutes, giving you complete control over your customer data.</em></p>
<img alt="OpenReplay" style="margin-top:5px; margin-bottom:5px;" width="768" height="400" src="https://raw.githubusercontent.com/openreplay/openreplay/main/static/openreplay-git-hero.svg" class="astro-UXNKDZ4E" loading="lazy" decoding="async">
<p><em>Happy debugging! <a href="https://openreplay.com" target="_blank">Try using OpenReplay today.</a></em><p>
<hr/>
</div>


[Mongoose](https://mongoosejs.com/docs/index.html) is an [Object Document Mapper (ODM)](https://www.dctacademy.com/blog/what-is-object-document-mapper-odm). Most developers use it because of its ability to simplify data management for [MongoDB](https://www.mongodb.com/cloud/atlas/register) and [Node.js](https://nodejs.org/en/download). Mongoose provides built-in validation capabilities, allowing you to ensure that data inserted into the database meets specific requirements. This reduces the risk of storing invalid or inconsistent data. Using the [Model](https://mongoosejs.com/docs/api/model.html), you can create reusable, object-oriented representations of your data, improving code organization and readability. You can also develop [Schema](https://mongoosejs.com/docs/guide.html) to organize data and handle MongoDB access. The plugin system also allows you to extend its functionality by adding custom features or behaviors. 

## Installing and Setting up Mongoose Globally

To get started, make sure you have installed MongoDB and Node.js.

Next, install from the command line using npm:

```nginx
npm install mongoose --save
```
Now that you have successfully installed your Mongoose package, we can start setting up our connection.

* Start by creating a folder for your application.

* In the same folder, create another file named `.env`, add the `MONGO_URI` variable, and paste the connection string there.  

*An overview of what your connection string looks like is shown below*   

![Screenshot 2024-04-30 091004](https://blog.openreplay.com/images/mongoose--simplifying-mongodb-for-node/images/image.png)


* Replace `<< password >>` with your database password.
* Now create a new folder, `db` and inside it a new file, `connectDB.js`. In this file, we'll write the code to connect with Mongoose and import it to our `app.js`.




> *Please be sure to store your database password created during the MongoDB setup process, securely. Failure to safeguard it could potentially grant hackers access to your database, enabling them to manipulate the stored information to their interest.*   
  

```javascript
const mongoose = require("mongoose");

const connectDB = (URI) => {
  return mongoose
 .connect(URI)
 .then(() => console.log("connected to the database"))
 .catch((err) => console.log(err));
};

module.exports = connectDB;
```
The first line of code includes the [Mongoose](https://mongoosejs.com/docs/index.html) library. Next, I open a connection to a database using the `connect()` function.

After creating your `connectDB.js` file, create another file named `app.js`. Here, we will import our `connectDB` file to successfully connect to the database.

```javascript
const connectDB = require("./db/connectDB");

const start = async () => {
 try {
 await connectDB(process.env.MONGO_URI);
 });
 } catch (error) {
 console.log(error);
 }
};

start();
```
Run your server, and you will get a text that looks like this connected to the database on your terminal.  

Now, you have completed your connection setup.

## Classes of Errors you can Encounter in Setting up your Mongoose Connection

Before establishing your connection, it's essential to create your MongoDB database cluster. This step is crucial to ensure a smooth connection setup. Failure to do so may result in errors while setting up your connection process. To gain insight into working with MongoDB, please refer to the [MongoDB documentation](https://www.mongodb.com/docs/).  

Two main classes of errors that can occur with the connection which are errors on the initial connection and errors after the initial connection was established.

### Error on initial connection 
The common reasons for this kind of error include:  
* Invalid connection string: Here, the connection string utilized for connecting to the MongoDB database may be incorrect or improperly formatted.  
* Network issues: Various issues, including restrictions from the firewall or network latency, may hinder the establishment of a connection.
* Server Unavailability: When the MongoDB server is offline or inaccessible due to network issues, it will result in an error.

To manage errors upon initial connection, you have two available options to consider.

Below is an example of a code.
```javascript
mongoose.connect('mongodb://localhost/mongoose_guide').
 catch(error => handleError(error));

// Or you can use the other option below:
try {
 await mongoose.connect('mongodb://localhost/mongoose_guide');
} catch (error) {
 handleError(error);
}
```
In the code above, we used our two available options to handle any error that might be encountered on our initial connection.  


### Error after the initial connection was established 

The common reasons for this kind of error include:  

* Query Errors: Database query errors may arise from issues such as incorrect query syntax, improper use of methods, or attempting operations on non-existent documents.  
* Documents Validation Errors: In document validation errors, you can create schemas and enforce validation rules for documents. If a document does not meet the validation procedures defined in its schema, it will result in an error.

```javascript
const getTasksId = async (req, res) => {
 try {
 const { id: taskID } = req.params;
 const task = await taskSchema.findById({ _id: taskID });
 if (!task) {
 return res.status(404).send(`Your _id : ${taskID} value is incorrect`);
 }
 res.status(200).json({ task });
 } catch (error) {
 res.status(500).json({ error });
 }
};
``` 
In the code above, we made use of our available option to handle errors when we want to retrieve any information from our database, and each time we send a request to get information from non-existent documents, a customized error message is sent back to us. To effectively manage errors once the initial connection is established, monitoring error events on the connection is important.

Effective error management is essential for creating reliable applications. Employing error-handling techniques such as try-catch blocks, error event listeners, and thorough console logging can assist in identifying and resolving issues without much stress.


## Working with Schemas: How to Create Custom Schemas

You set up data structures using JavaScript objects. These structures act like templates for your data in MongoDB. For example, defining a basic schema might look like this:

```javascript
const mongoose = require('mongoose');

const studentSchema = new mongoose.Schema({
 department : String,
 name: String,
 degree: String,
 contact: Number,
});

// below is another structure of schema

const taskSchema = new mongoose.Schema({
 name: {
 type: String,
 required: [true, "please enter your name"],
 trim: true,
 maxlength: [30, "maximum character is 30"],
 },
 completed: {
 type: Boolean,
 default: false,
 },
 email: {
 type: String,
 // required: [true, "please enter your email"],
 },
});
```

Some schema types are strictly accepted: string, number, date, buffer, boolean, mixed, objectId, array, decimal128, map, and uuid. You have now created and defined your schema. 


<CTA_Middle_Frameworks />



## How to Create a Custom Model using Mongoose
A model is a constructor function representing a document within a MongoDB collection.

Once your schema has been defined, you are given access to create models based on that schema. Models are tasked with performing interactions with our MongoDB collections when we perform CRUD operations. Creating a model for our schema shouldn't be a big deal. Here is an example of how to create your model.

```javascript
const mongoose = require('mongoose');

// Define a schema for the documents in the collection
const taskSchema = new mongoose.Schema({
 name: {
 type: String,
 required: [true, "please enter your name"],
 trim: true,
 maxlength: [30, "maximum character is 30"],
 },
 completed: {
 type: Boolean,
 default: false,
 },
 email: {
 type: String,
 // required: [true, "please enter your email"],
 },
});

// Create a model based on the schema
const Task  = mongoose.model('Task', taskSchema)
```

Hope you enjoyed defining and creating your first schema and model, now we go into how to implement the powerful CRUD operations.

## Emphasis on how to Perform CRUD Operations in Mongoose
There are wide range of methods to perform `create`, `read`, `update` and `delete` (CRUD) operations on your data in the database, by using these operations, users have the liberty to `create` data, have access to the data in the user interface to be able `read` the data, `update` and `delete` the data.

* **Using `Create()` operation**  
The create operation is used to create new model documents. However, it is also a shorthand for making one or more documents in the database. Below is a code example.  
 
```javascript
const createProfile = async (req, res) => {
 try {
 const profile = await studentSchema.create({ department : 'Computer Science' });
 res.status(201).json({ profile });
 } catch (error) {
 res.status(500).json({ error });
 }
};
```
From the code above we created a profile that houses our student's department in the database.

* **Using `Read()` operation**  
Read operation grants you access to view all the information on your database. Using the `find()` method, we can retrieve information in the database that matches the query to obtain and read the information stored in the database.

```javascript
const readProfile = async (req, res) => {
 try {
 const profile = await studentSchema.find({});
 res.status(200).json({ profile });
 } catch (error) {
 res.status(500).json({ error });
 }
};
```
* **Using `Update()` operation**  
The update operation searches for the queried record first in the database. It retrieves and then modifies the data according to the user's choice. You can use different methods, such as `updateMany()` `findByIdAndUpdate()`, `findOneAndUpdate()`, and `updateOne()`, to update the data in our database. However, in this article, we will focus only on the `findByIdAndUpdate()` method to update a certain data by providing its data _id. Below is a code sample.  
 
```javascript
const updateProfile = async (req, res) => {

 const profile = await studentSchema.findOneAndUpdate(
 {_id: profileID},{department:'Electrical Engineering'},{
 new: true,
 runValidators: true,
 }
 );

 if (!profile) {
 return res
 .status(404)
 .send(
 `Unable to update the id, make sure you have entered the correct id`
 );
 }
 res.status(200).json({ profile });

};
```      

When we create information on the collection section in MongoDB, it automatically creates an `_id` for us. This code finds that particular `_id` and updates our department value, initially Computer Science to Electrical Engineering. If the `_id` is not found an error is thrown back to us.  


*  **Using `Delete()` operation**  
Delete operation gives us many options which we can use to delete our information from the database. Here we will focus only on `findByIdAndDelete()`. Check out the code below for better understanding.

```javascript
const deleteProfile = async (req, res) => {
 try {
 const profile = await studentSchema.findByIdAndDelete({_id: profileID},{department:'Electrical Engineering'},);
 if (!profile) {
 return res
 .status(404)
 .send(
 `in other to delete the id, please provide the correct id`
 );
 }
 res.status(200).send(` Id has been deleted`);
      
 } catch (error) {
 res.status(500).json({ error });
 }
};
```

This code obtains the information from the particular id you requested to delete and makes sure the information contained in that particular id is deleted from the database.

## Queries: Understanding how to Execute Mongoose Queries
While performing our CRUD operations earlier on you have encountered queries. In this section of the article, I will explain briefly with code examples, focusing only on a few queries to ensure you understand it better.

* `find()`  
```javascript
await studentSchema.find({ score: { $gte: 75 } }).exec()
```
This code executes and finds all the students with scores greater than or equal to 75.

* `deleteMany()`  

```javascript
await studentSchema.deleteMany({score: { $lt: 25 } });
```
This code executes and deletes all the students with scores less than 25.

With these few examples illustrated above, I believe you've understood how to work with queries. To learn about other query options, visit [Mongoose Queries](https://mongoosejs.com/docs/queries.html).

## Mongoose Plugins: Illustrating how to Register a Global Plugin for all Schemas

Plugins offer a convenient way to reuse logic across multiple schemas. Assuming you have more than one model in your database and you wish to include a "loadedAt" property in each of them. By creating a plugin once, you can apply it to all the schemas. An illustration of the code example is below.

```javascript
const mongoose = require('mongoose');
mongoose.plugin(require('./loadedAt'));

const studentSchema = new Schema({ /* ... */ });

const developerSchema = new Schema({ /* ... */ });

const Student = mongoose.model('Student', studentSchema);

const Developer = mongoose.model('Developer', developerSchema);
```
Our global plugin has successfully added "loadedAt" property to both our `studentSchema` and `developerSchema`.

## Conclusion
After reviewing this article, you should be equipped with knowledge about Mongoose schemas and Models, implement them, and be able to manage document creation and updates using CRUD efficiently.

By now, you should feel confident in your understanding of Mongoose. For further insights, consider exploring the  [Mongoose Guides](https://mongoosejs.com/docs/guides.html), which explore more advanced concepts like validation, population, middleware, and more.