Back

Mongoose: simplifying MongoDB for Node.js

Mongoose: simplifying MongoDB for Node.js

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.

Mongoose is an Object Document Mapper (ODM). Most developers use it because of its ability to simplify data management for MongoDB and Node.js. 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, you can create reusable, object-oriented representations of your data, improving code organization and readability. You can also develop Schema 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:

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

  • 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.

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 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.

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.

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.

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.
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:

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.

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.

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.
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.
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.
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.
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()
await studentSchema.find({ score: { $gte: 75 } }).exec()

This code executes and finds all the students with scores greater than or equal to 75.

  • deleteMany()
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.

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.

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, which explore more advanced concepts like validation, population, middleware, and more.

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay