# How to connect with MongoDB using Mongoose in Node.js

By Atta Ur Rehman Shah (https://attacomsian.com/about). Published Dec 11, 2020, updated Oct 28, 2022. Topics: Node.js, Mongoose, MongoDB.
Canonical URL: https://attacomsian.com/blog/nodejs-getting-started-with-mongoose

> Learn what is Mongoose and how to use it to model application data for querying, creating, updating, and removing documents from MongoDB.

[Mongoose](https://mongoosejs.com/) is an object data modeling (ODM) tool for MongoDB and Node.js. It is designed to work in an asynchronous environment and supports both [promises](https://attacomsian.com/blog/promises-javascript) and callbacks.

Mongoose offers all 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.

## Prerequisites

Before we get started, make sure that you have the following:

1. [Node.js](https://attacomsian.com/blog/install-nodejs-npm-ubuntu) is installed and configured on your machine.
2. A MongoDB instance running on your machine (installation instructions for [Mac](https://attacomsian.com/blog/install-mongodb-macos) and [Ubuntu](https://attacomsian.com/blog/install-mongodb-ubuntu-18-04)).

## Installing Mongoose

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

```bash
$ npm install mongoose --save
```

Now you can require Mongoose in your application:

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

## Connecting to MongoDB

[Connecting to a MongoDB database](https://attacomsian.com/blog/nodejs-mongodb-local-connection#connecting-with-mongodb) is incredibly simple with Mongoose. For applications that use only one database, Mongoose provides the `connect()` method:

```javascript
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}`)
  .then(() => {
    console.log('MongoDB connected!!')
  })
  .catch(err => {
    console.log('Failed to connect to MongoDB', err)
  })
```

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 document structure, document properties, default values, validators, and more.

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

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

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

Here is the list of all valid [schema types](https://attacomsian.com/blog/mongoose-schema-types) that Mongoose supports:

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

Read [this article](https://attacomsian.com/blog/mongoose-schemas) to learn more about Mongoose schema and how to define indexes, static and virtual methods, query helpers, aliases, and more.

## Defining a Model

A [Mongoose model](https://attacomsian.com/blog/mongoose-models) is what you actually need in order to interact with MongoDB. It is a compiled schema version 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:

```javascript
const Book = mongoose.model('Book', bookSchema)
```

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](https://attacomsian.com/blog/working-with-mongodb-using-mongo-shell#list-documents), you would use the `db.books.find()` command.

## CRUD Operations

Once you have created the model, you are ready to perform basic CRUD operations, aggregations, counts, and more.

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

### Creating a document

To create a new document, 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:

```javascript
const book = new Book({
  name: 'Introduction to Node.js',
  author: 'Atta',
  isbn: 'ABL-4566'
})

book
  .save()
  .then(book => {
    console.log(book)
  })
  .catch(err => {
    console.log(err)
  })
```

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

```bash
{
  _id: 5fd329cad20db9eefbd96262,
  name: 'Introduction to Node.js',
  author: 'Atta',
  isbn: 'ABL-4566',
  created: 2020-12-11T08:11:54.236Z,
  __v: 0
}
```

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

### 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 attributes defined in the schema and even combine multiple properties. 

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

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

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

```bash
[
  {
    _id: 5fd329cad20db9eefbd96262,
    name: 'Introduction to Node.js',
    author: 'Atta',
    isbn: 'ABL-4566',
    created: 2020-12-11T08:11:54.236Z,
    __v: 0
  }
]
```

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

```javascript
 Book.find()
  .then(books => {
    console.log(books)
  })
  .catch(err => {
    console.log(err)
  })
```

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

```javascript
Book.findById('5fd329cad20db9eefbd96262')
  .then(book => {
    console.log(book)
  })
  .catch(err => {
    console.log(err)
  })
```

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

```bash
{
  _id: 5fd329cad20db9eefbd96262,
  name: 'Introduction to Node.js',
  author: 'Atta',
  isbn: 'ABL-4566',
  created: 2020-12-11T08:11:54.236Z,
  __v: 0
}
```

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

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

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

### 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 &mdash; all in one transaction:

```javascript
Book.findOneAndUpdate({ author: 'Atta' }, { author: 'John' }, { new: true })
  .then(book => {
    console.log(book)
  })
  .catch(err => {
    console.log(err)
  })
```

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:

```bash
{
  _id: 5fd329cad20db9eefbd96262,
  name: 'Introduction to Node.js',
  author: 'John',
  isbn: 'ABL-4566',
  created: 2020-12-11T08:11:54.236Z,
  __v: 0
}
```

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

```javascript
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)
  })
```

### Deleting a document

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

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

The deleted document returned will look like the following:

```bash
{
  _id: 5fd329cad20db9eefbd96262,
  name: 'Node.js Basics',
  author: 'John',
  isbn: 'ABL-4566',
  created: 2020-12-11T08:11:54.236Z,
  __v: 0
}
```

## Query Building

Mongoose provides an excellent API that allows building query components incrementally. You can use the query builder to run many complex operations supported by MongoDB.

Let us look at the following query:

```javascript
Book.find()                 // Find all books
  .skip(10)                 // Skip the first 10 books
  .limit(5)                 // Limit to 5 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)
  })
```

The above query does the following in a single operation:

1. Find all books in the collection
2. Skip the first 10 documents
3. Limit the result to the top 5 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](https://www.npmjs.com/package/mongodb) 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. 

Read [this article](https://attacomsian.com/blog/working-with-mongodb-using-mongo-shell) to learn how to access and query MongoDB using the Mongo shell.
