Mongoose 101
Tags: javascript, mongoose, node, mongodb
Published: 2019-12-18 02:30:45

---
title: Mongoose 101
description: Mongoose is a library that makes MongoDB easier to use. In this article, you'll learn how to use Mongoose on a basic level.
canonical_url: https://zellwk.com/blog/mongoose
cover_image: https://zellwk.com/images/2019/mongoose/three-ryus.png
tags: javascript, mongoose, node, mongodb
series: mongoose
published: true
---

Mongoose is a library that makes MongoDB easier to use. It does two things:

1. It gives structure to MongoDB Collections
2. It gives you helpful methods to use

In this article, you'll learn how to use Mongoose on a basic level.

## Prerequisites

I assume you have done the following:

1. You have installed MongoDB on your computer
2. You know how to set up a local MongoDB connection
3. You know how to see the data you have in your database
4. You know what are "collections" in MongoDB

If you don't know any of these, please read ["How to set up a local MongoDB connection"][1] before you continue.

I also assume you know how to use MongoDB to create a simple CRUD app. If you don't know how to do this, please read ["How to build a CRUD app with Node, Express, and MongoDB"][2] before you continue.

## Connecting to a database

First, you need to download Mongoose.

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

You can connect to a database with the `connect` method. Let's say we want to connect to a database called `street-fighters`. Here's the code you need:

```js
const mongoose = require("mongoose");
const url = "mongodb://127.0.0.1:27017/street-fighters";

mongoose.connect(url, { useNewUrlParser: true });
```

We want to know whether our connection has succeeded or failed. This helps us with debugging.

To check whether the connection has succeeded, we can use the `open` event. To check whether the connection failed, we use the `error` event.

```js
const db = mongoose.connection;
db.once("open", _ => {
  console.log("Database connected:", url);
});

db.on("error", err => {
  console.error("connection error:", err);
});
```

Try connecting to the database. You should see a log like this:

<figure role="figure"><img src="https://zellwk.com/images/2019/mongoose/connect-database.png" alt="Connected to a database."></figure>

## Creating a Model

In Mongoose, you need to **use models to create, read, update, or delete items** from a MongoDB collection.

To create a Model, **you need to create a Schema**. A Schema lets you** define the structure of an entry** in the collection. This entry is also called a document.

Here's how you create a schema:

```js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;

const schema = new Schema({
  // ...
});
```

You can use [10 different kinds of values][3] in a Schema. Most of the time, you'll use these six:

- String
- Number
- Boolean
- Array
- Date
- ObjectId

Let's put this into practice.

Say we want to create characters for our Street Fighter database.

In Mongoose, it's a normal practice to **put each model in its own file.** So we will create a `Character.js` file first. This `Character.js` file will be placed in the `models` folder.

```bash
project/
    |- models/
        |- Character.js
```

In `Character.js`, we create a `characterSchema`.

```js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;

const characterSchema = new Schema({
  // ...
});
```

Let's say we want to save two things into the database:

1. Name of the character
2. Name of their ultimate move

Both can be represented with Strings.

```js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;

const characterSchema = new Schema({
  name: String,
  ultimate: String
});
```

Once we've created `characterSchema`, we can use mongoose's `model` method to create the model.

```js
module.exports = mongoose.model("Character", characterSchema);
```

## Creating a character

Let's say you have a file called `index.js`. This is where we'll perform Mongoose operations for this tutorial.

```bash
project/
    |- index.js
    |- models/
        |- Character.js
```

First, you need to load the Character model. You can do this with `require`.

```js
const Character = require("./models/Character");
```

Let's say you want to create a character called Ryu. Ryu has an ultimate move called "Shinku Hadoken".

To create Ryu, you use the `new`, followed by your model. In this case, it's `new Character`.

```js
const ryu = new Character({
  name: "Ryu",
  ultimate: "Shinku Hadoken"
});
```

`new Character` creates the character in memory. It has not been saved to the database yet. **To save to the database, you can run the `save` method**.

```js
ryu.save(function(error, document) {
  if (error) console.error(error);
  console.log(document);
});
```

If you run the code above, you should see this in the console.

<figure role="figure"><img src="https://zellwk.com/images/2019/mongoose/saved.png" alt="Ryu saved to the database."></figure>

### Promises and Async/await

**Mongoose supports promises.** It lets you write nicer code like this:

```js
// This does the same thing as above
function saveCharacter(character) {
  const c = new Character(character);
  return c.save();
}

saveCharacter({
  name: "Ryu",
  ultimate: "Shinku Hadoken"
})
  .then(doc => {
    console.log(doc);
  })
  .catch(error => {
    console.error(error);
  });
```

You can also use the `await` keyword if you have an asynchronous function.

If the Promise or Async/Await code looks foreign to you, I recommend reading ["JavaScript async and await"][4] before continuing with this tutorial.

```js
async function runCode() {
  const ryu = new Character({
    name: "Ryu",
    ultimate: "Shinku Hadoken"
  });

  const doc = await ryu.save();
  console.log(doc);
}

runCode().catch(error => {
  console.error(error);
});
```

Note: I'll use the async/await format for the rest of the tutorial.

## Uniqueness

Mongoose adds a new character to the database each you use `new Character` and `save`. If you run the code(s) above three times, you'd expect to see three Ryus in the database.

<figure role="figure"><img src="https://zellwk.com/images/2019/mongoose/three-ryus.png" alt="Three Ryus in the database."></figure>

We don't want to have three Ryus in the database. We want to have **ONE Ryu only**. To do this, we can use the **unique** option.

```js
const characterSchema = new Schema({
  name: { type: String, unique: true },
  ultimate: String
});
```

The `unique` option **creates a unique index**. It ensures we cannot have two documents with the same value (for `name` in this case).

For `unique` to work properly, you need to **clear the Characters collection**. To clear the Characters collection, you can use this:

```js
await Character.deleteMany({});
```

Try to add two Ryus into the database now. You'll get an `E11000 duplicate key error`. You won't be able to save the second Ryu.

<figure role="figure"><img src="https://zellwk.com/images/2019/mongoose/dup-error.png" alt="Duplicated key error.png"></figure>

Let's add another character into the database before we continue the rest of the tutorial.

```js
const ken = new Character({
  name: "Ken",
  ultimate: "Guren Enjinkyaku"
});

await ken.save();
```

<figure role="figure"><img src="https://zellwk.com/images/2019/mongoose/ryu-ken.png" alt="Database contains two characters."></figure>

## Retrieving a character

Mongoose gives you two methods to find stuff from MongoDB.

1. `findOne`: Gets one document.
2. `find`: Gets an array of documents

### findOne

`findOne` **returns the first document** it finds. You can specify any property to search for. Let's search for `Ryu`:

```js
const ryu = await Character.findOne({ name: "Ryu" });
console.log(ryu);
```

<figure role="figure"><img src="https://zellwk.com/images/2019/mongoose/found-one.png" alt="Found Ryu from the database."></figure>

### find

`find` **returns an array** of documents. If you specify a property to search for, it'll return documents that match your query.

```js
const chars = await Character.find({ name: "Ryu" });
console.log(chars);
```

<figure role="figure"><img src="https://zellwk.com/images/2019/mongoose/found-many-ryu.png" alt="Combed through the database and found one character with the name Ryu."></figure>

If you did not specify any properties to search for, it'll return an array that contains all documents in the collection.

```js
const chars = await Character.find();
console.log(chars);
```

<figure role="figure"><img src="https://zellwk.com/images/2019/mongoose/found-many.png" alt="Found two characters in the database."></figure>

## Updating a Character

Let's say Ryu has three special moves:

1. Hadoken
2. Shoryuken
3. Tatsumaki Senpukyaku

We want to add these special moves into the database. First, we need to update our `CharacterSchema`.

```js
const characterSchema = new Schema({
  name: { type: String, unique: true },
  specials: Array,
  ultimate: String
});
```

Then, we use one of these two ways to update a character:

1. Use `findOne`, then use `save`
2. Use `findOneAndUpdate`

### findOne and save

First, we use `findOne` to get Ryu.

```js
const ryu = await Character.findOne({ name: "Ryu" });
console.log(ryu);
```

Then, we update Ryu to include his special moves.

```js
const ryu = await Character.findOne({ name: "Ryu" });
ryu.specials = ["Hadoken", "Shoryuken", "Tatsumaki Senpukyaku"];
```

After we modified `ryu`, we run `save`.

```js
const ryu = await Character.findOne({ name: "Ryu" });
ryu.specials = ["Hadoken", "Shoryuken", "Tatsumaki Senpukyaku"];

const doc = await ryu.save();
console.log(doc);
```

<figure role="figure"><img src="https://zellwk.com/images/2019/mongoose/ryu-updated.png" alt="Updated Ryu."></figure>

### findOneAndUpdate

`findOneAndUpdate` is the same as MongoDB's `findOneAndModify` method.

Here, you search for Ryu and pass the fields you want to update at the same time.

```js
// Syntax
await findOneAndUpdate(filter, update);
```

```js
// Usage
const doc = await Character.findOneAndUpdate(
  { name: "Ryu" },
  {
    specials: ["Hadoken", "Shoryuken", "Tatsumaki Senpukyaku"]
  }
);

console.log(doc);
```

<figure role="figure"><img src="https://zellwk.com/images/2019/mongoose/ryu-updated.png" alt="Updated Ryu."></figure>

### Difference between findOne + save vs findOneAndUpdate

Two major differences.

First, the **syntax for `` findOne` + `save `` is easier to read** than `findOneAndUpdate`.

Second, `findOneAndUpdate` does not trigger the `save` middleware.

**I'll choose `findOne` + `save`** over `findOneAndUpdate` anytime because of these two differences.

## Deleting a character

There are two ways to delete a character:

1. `findOne` + `remove`
2. `findOneAndDelete`

### Using findOne + remove

```js
const ryu = await Character.findOne({ name: "Ryu" });
const deleted = await ryu.remove();
```

### Using findOneAndDelete

```js
const deleted = await Character.findOneAndDelete({ name: "Ken" });
```

## Quick summary

You learned how to use Mongoose to:

1. Connect to a database
2. Create, Read, Update, and Delete documents

[1]: https://zellwk.com/blog/local-mongodb
[2]: https://zellwk.com/blog/crud-express-mongodb
[3]: https://mongoosejs.com/docs/guide.html
[4]: https://zellwk.com/blog/async-await

<hr>

Thanks for reading. This article was originally posted on [my blog](https://zellwk.com/blog/mongoose). Sign up for my newsletter if you want more articles to help you become a better frontend developer.