codedamn

NodeJs Mongoose tutorial

  • Krishnaditya Biswas's profile image
    Krishnaditya Biswas
    Author
NodeJs Mongoose tutorial

Mongoose is an object document modeling (ODM) layer that sits on top of the Node.Js MongoDB API. If you’re coming from an SQL background, then Mongoose is similar to an ORM (Object-relational Mapping).

It is designed to work in an asynchronous environment and supports both promises and callbacks. It’s not mandatory to use Mongoose over the MongoDB Native API. However, there are some benefits to doing so.

Introduction

MongoDB server listens on a TCP socket (usually), and your Node.js process can connect to it using a TCP connection. On top of this, MongoDB also has its own protocol for understanding what exactly the client (our Node.js process) wants the database to do.

For this communication, instead of learning the messages we have to send on the TCP layer, we abstract that away with the help of a “driver” software, called MongoDB driver in this case.

Mongoose has all sets of features that you need to model your application data. It includes built-in type casting, schema validation, query building, business logic hooks, and relationship management between data.

Before we get started with Mongoose, make sure that you have already installed and configured both Node.js and MongoDB.

Installing Mongoose

You can install Mongoose in your Node.js project with the following command:

$ npm install mongoose --save
Code language: JavaScript (javascript)

Now you can require Mongoose in your application:

const mongoose = require('mongoose');
Code language: JavaScript (javascript)

Connecting to MongoDB

Connecting to a database is incredibly simple with Mongoose. For applications that use only one database, Mongoose provides the connect() method:

const mongoose = require('mongoose'); const server = '127.0.0.1:27017'; // REPLACE WITH YOUR OWN SERVER const database = 'test'; // REPLACE WITH YOUR OWN DB NAME mongoose.connect(`mongodb://${server}/${database}`, { useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: false, useCreateIndex: true }).then(() => { console.log('MongoDB connected!!'); }).catch(err => { console.log('Failed to connect to MongoDB',err); });
Code language: JavaScript (javascript)

If you need to connect with additional databases, use the mongoose.createConnection() method.

Defining the Schema

Before you can do anything with Mongoose, you need to define a schema. A Mongoose schema defines the structure of the document, document properties, default values, validators, and more.

All the key names in a schema correspond to the properties names in a MongoDB collection. For example, here is what a Book the schema might potentially look like:

const bookSchema = new mongoose.Schema({ name: { type: String, required: true }, author: String, isbn: { type: String, unique: true }, created: { type: Date, default: Date.now } });
Code language: JavaScript (javascript)

As you can see above, we have defined several properties in bookSchema. We have also specified several constraints required and unique. If any of these constraints are violated when saving the document, the operation will fail and an error will be thrown.

Here is the list of all valid schema types that Mongoose supports:

  • String
  • Number
  • Date
  • Buffer
  • Boolean
  • Mixed
  • ObjectId
  • Array
  • Decimal128
  • Map
  • Schema

Defining a Model

A Mongoose model is what you actually need in order to interact with MongoDB. It is the compiled version of the schema that maps directly to a single document in the collection.

You can use a Mongoose model for updating, creating, querying, and removing documents from a MongoDB collection.

To create a Book model for the bookSchema defined above, you can use the mongoose.model() method and pass it the name of the collection and a reference to the schema definition:

const Book = mongoose.model('Book', bookSchema);
Code language: JavaScript (javascript)

Note that the mongoose.model() method will pluralize and lowercase the name of the collection you specified. For example, the actual MongoDB collection name for the Book model should be books. To retrieve all books using the Mongo Shell, you would use the db.books.find() command.

CRUD Operations

Once you have created the model, you can be ready to perform basic CRUD operations, plus other operations like aggregations, count, and more.

Mongoose provides a very flexible API to perform different operations in many ways.

Creating a document

To create a new document, just create a new instance of the model and then save it to the database by calling the save() method on the object.

Here is an example:

const book = new Book({ name: 'Introduction to Node.js', author: 'ABCD', isbn: 'ABL-4566' }); book.save() .then(book => { console.log(book); }).catch(err => { console.log(err); });
Code language: JavaScript (javascript)

The result is a document that is returned upon successful creation:

{ _id: 5fd329cad20db9eefbd96262, name: 'Introduction to Node.js', author: 'ABCD', isbn: 'ABL-4566', created: 2022-10-11T08:11:54.236Z, __v: 0 }
Code language: JavaScript (javascript)

The _id field is auto-generated by MongoDB and acts as a primary key of the collection. It’s unique across all documents in the collection.

The __v field is the version key property set by Mongoose when the document was created. Its value is the internal revision of the document.

Finding a document

Finding a document using Mongoose is very simple. The model class exposes several static and instance methods to perform query operations.

You can query documents by any of the attributes defined in the schema, and even combine multiple properties together.

The following example demonstrates how you can use the find() method to find one or more documents:

Book.find({ isbn: 'ABL-4566' }) .then(book => { console.log(book); }).catch(err => { console.log(err); });
Code language: JavaScript (javascript)

The document returned will be similar to what was displayed when we first created it but wrapped in an array:

[ { _id: 5fd329cad20db9eefbd96262, name: 'Introduction to Node.js', author: 'ABCD', isbn: 'ABL-4566', created: 2022-10-11T08:11:54.236Z, __v: 0 } ]
Code language: JavaScript (javascript)

The find() method can also be used to find all documents in a collection:

Book.find() .then(books => { console.log(books); }).catch(err => { console.log(err); });
Code language: JavaScript (javascript)

If you already know the value of the _id attribute, use findById() method instead:

Book.findById('5fd329cad20db9eefbd96262') .then(book => { console.log(book); }).catch(err => { console.log(err); });
Code language: JavaScript (javascript)

The findById() method returns a single document that matches the _id attribute key:

{ _id: 5fd329cad20db9eefbd96262, name: 'Introduction to Node.js', author: 'ABCD', isbn: 'ABL-4566', created: 2022-10-11T08:11:54.236Z, __v: 0 }
Code language: JavaScript (javascript)

Alternatively, you could also use the findOne() method that allows you to use logical $or and $and operators.

For example, we can query Book model by author’s name and book’s title. If a document is found that matches both fields, it will be returned:

Book.findOne({ $and: [{ author: 'Muller' }, { name: 'Web Security' }] }) .then(book => { console.log(book); }).catch(err => { console.log(err); });
Code language: JavaScript (javascript)

Updating an existing document

To update an existing document, you use the findOneAndUpdate() method.

As the name suggests, this method finds the matching document in the collection, and updates it — all in one transaction:

Book.findOneAndUpdate( { author: 'ABCD' }, { author: 'John' }, { new: true }) .then(book => { console.log(book); }).catch(err => { console.log(err); });
Code language: JavaScript (javascript)

By default, Mongoose does not return the updated document due to performance reasons. But we can ask for it by passing an additional { new: true } parameter.

The document returned by the above query will have the author field updated:

{ _id: 5fd329cad20db9eefbd96262, name: 'Introduction to Node.js', author: 'John', isbn: 'ABL-4566', created: 2022-10-11T08:11:54.236Z, __v: 0 }
Code language: JavaScript (javascript)

You could also first retrieve the document, update the fields, and then call the save() method to save changes:

Book.findOne({ isbn: 'ABL-4566' }) .then(book => { // Update Fields book.name = 'Node.js Basics'; // Save Changes book.save() .then(doc => console.log(doc)) .catch(err => console.log(err)); }).catch(err => { console.log(err); })
Code language: JavaScript (javascript)

Deleting a document

You can use the findOneAndRemove() method to delete a document from a collection. It returns the original document that was removed:

Book.findOneAndDelete({ author: 'John' }) .then(book => { console.log(book); }).catch(err => { console.log(err); });
Code language: JavaScript (javascript)

The deleted document returned will look like the following:

{ _id: 5fd329cad20db9eefbd96262, name: 'Node.js Basics', author: 'John', isbn: 'ABL-4566', created: 2020-12-11T08:11:54.236Z, __v: 0 }
Code language: JavaScript (javascript)

Query Building

Mongoose provides an excellent API that allows incrementally building query components. You can use the query builder to run many complex operations supported by MongoDB.Imagine that you have to parse documents of 10GB in total size on a 1GB/1core cloud server. You cannot fetch the whole collection because that will not fit on your system. The cursor is a good (and the only?) option here

Cursor-based querying

Cursor-based querying means that you work with a single record at a time while you fetch a single or batch of documents at a time from the database. This is an efficient way of working with huge amounts of data in a limited memory environment.

Imagine that you have to parse documents of 10GB in total size on a 1GB/1core cloud server. You cannot fetch the whole collection because that will not fit on your system. The cursor is a good (and the only?) option here.

Full fetching querying

This is the type of query where you get the full response of your query all at once. For the most part, this is what you’ll be using. Therefore, we’ll be focusing mostly on this method here.

Let us look at the following query:

Book.find() // Find all books .skip(8) // Skip the first 8 books .limit(4) // Limit to 4 books only .sort({ name: 1 }) // Sort ascending by name .select({ name: true }) // Select book name only .exec() // Execute the query .then(books => { console.log(books); }) .catch(err => { console.error(err); });
Code language: JavaScript (javascript)

The above query does the following in a single operation:

  1. Find all books in the collection
  2. Skip the first 8 documents
  3. Limit the result to the top 4 documents
  4. Sort the result by book’s name
  5. Select the name field
  6. Execute the query and return the result

Conclusion

Mongoose is a popular object data modeling tool for Node.js and MongoDB. It provides a very flexible yet powerful API to create, update, query, and remove documents from MongoDB.

While you can use the MongoDB native driver to interact directly with the database, Mongoose simplifies the process by providing a higher abstraction layer. Under the hood, Mongoose also uses the MongoDB native driver.

Have a nice day!

Learn programming on codedamn

Codedamn is an interactive coding platform with tons of sweet programming courses that can help you land your first coding job. Here's how:

Programming is one of the most in-demand jobs today. Learning to program can change your future. All the best!