Connecting to MongoDB using Mongoose 🌳
Tags: mongodb, webdev, node, javascript
Published: 2020-07-21 13:15:38

This is the second part of the "How to write an Authentication API using MongoDB and Express" series. 🌈

You can check out the First Part [here](https://dev.to/aritik/writing-a-basic-authentication-api-with-express-and-mongodb-1-j8)

In this part , I'm going to cover the simplest approach to connecting to MongoDB using the Mongoose ODM. 

Mongoose provides different lifecycle methods as well as simple ways to write out elaborate Schemas that interface with your MongoDB collections. We'll make use of this functionality today and define our `User Schema` using Mongoose.

## Setting up a basic server using Express

#### Prerequisites 🌻

1. [NodeJS](https://nodejs.org) installed on your system.
2. `npm` installed on your system.
3.  Any Code Editor (Visual Studio Code, Sublime)
4. Postman for testing out our API. (or Insomnia , VSC REST CLIENT)
5. Terminal

####Initializing a Repository 🌈

`cd` into your preferred directory and run `npm init -y`.
The *-y* flag says yes to all the options when npm prompts you to fill out the *package.json* file.

npm is a package manager that allows us to add , use and manage libraries , frameworks and additional packages in our Project.

Run `npm i express mongoose` to install express and mongoose. This will add express and mongoose to your project. You can check if they're installed by inspecting your *package.json* file.

Let's also install two additional devDependencies. devDependencies are only used for development purposes and are not shipped in production. To install packages as devDependencies, we use the `--save-dev` flag.
`npm i nodemon morgan --save-dev`.

We'll use nodemon to reload our server everytime we make changes to our `server.js` file. we'll use Morgan to monitor HTTP requests made to our server.

Go to your `package.json` file and add ` "start": "nodemon server.js",` under scripts.

Your `package.json` should look like this at the end.

```JSON
{
  "name": "server",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "nodemon server.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "dotenv": "^8.2.0",
    "express": "^4.17.1",
    "mongoose": "^5.9.25",
    
  },
"devDependencies" : {
  "nodemon": "^2.0.4".
  "morgan": "^1.10.0"
}
}

```
#### Setting up an Express server

Let's set up a simple Express server. In your project directory , create a `server.js` file and add the following code.

```Javascript
const express = require('express');

const app = express();

const morgan = require('morgan'); 
app.use(morgan('tiny'));

app.listen(5000, () => {
	console.log("Server started on PORT : ", port);
})
```

In the code above , we bring in the dependencies we require by using the `require(<package_name>)` syntax and then assign it to a variable.
we invoke the express() function and store it's return value in app.

```Javascript
const app = express()
```

We also require the `morgan` package. Since `morgan` is a middleware , we use it using the `app.use(<middleware>)` method. We also pass `'tiny'` parameter. There are different values you can pass as parameters to the morgan function. You can read more about morgan [here](https://www.npmjs.com/package/morgan).

Go back to your terminal and run `npm start` to startup your server and you should see the following message.
```zsh
> npm start
Server started on PORT : 5000
```

## Creating a MongoDB Cluster 🌈
Let's head to [https://www.mongodb.com](https://www.mongodb.com) and set up a cluster for personal use. 

Create an account and sign in to it. On signing in , you should see a button on the right that says **Create a New Cluster**

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/13mz6bs49mzx1v7zuj33.png)

In the next screen , choose any Cloud provider and choose a Region with a *free-tier* that's closest to you. 

Once you're satisfied with your options , proceed to create a cluster. It will take 3-5 minutes to create a cluster and soon , you'll see your newly created cluster on the home screen.

Now , before we can connect to our cluster , we need to make a few more adjustments.

Go to the *Database Access* tag and click on **Add New Database User**

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/rkumg97yac8xufx40skj.png)

Select the method of authentication as *Password* and fill out a username and password for the DB user.

Make sure to jot down the password and username somewhere for later , when we need to connect to our Database.



Set the user's privileges to *Read and Write to Database* and click on the **Add User** Button. You can now see that the user has been added. 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/g2sgnz0qvv884xcxkv2a.png)

Finally, click on the Network Access tab in the left pane and click on the *Add IP Address* Button.

On the pane , that pops up , click on the *Add Current IP Address* Button and then click on the confirm button.

With this , we're done with setting up our MongoDB Cluster.

Let's copy our connection string next. 

Click on the connect button

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ikbfrpcbluuw4gwss9xt.png)

Let's connect to MongoDB using their native drivers.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/glls95gxd9ymzg5j4uew.png)


Copy the connection string.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ys9imj8ub7ix738ffi9m.png)

**Note** ⚠️
I have opted to use the connection string for NodeJS version `2.2.12` or later as opposed to the latest connection string. While not all people would run into this problem , I had troubles connecting with the latest Node connection string when using Mongoose.




## Connecting using Mongoose.connect 🌈

Before connecting to our Database , lets install an additional dependency to our project. Stop your server using `ctrl + C` and run `npm i dotenv`.

Dotenv is a package that allows us to store sensitive information such as usernames and passwords in a `.env` file as **environment variables**. 

In your project , create a `.env` file. To define an environment variable , simple type in the name of the variable , an equal-to sign and then the value.

`VARIABLE_NAME = your_password` 

Now we can use the environment variable anywhere in our project by simply requiring the `dotenv` package. 

```Javascript
require('dotenv').config();
```
To use an environment variable , 

```Javascript
const port = PROCESS.env.VARIABLE_NAME;
```

Let's setup our environment variables.
In our `.env` file , add the connection string , password , username and let's also add a name for our database.

In our connection string , erase the beginning string `username:<password>@`.

We will pass the username and password separately as parameters when we're connecting to our database.

Define the following env variables and fill in the values as required.

```env
DB_URI = <your_connection_string>
DB_NAME = <any_db_name>
DB_USER = <your_username>
DB_PASSWORD = <your_password>
```

Note that the username and password are not the username and password of your MongoDB account. They're the username and password you set , when you created the DB user.



We'll now define our `initDB.js` file. We'll set up a separate file so you can add this file to any future backend projects that require connecting to mongoDB. Separating different aspects of your code also improves readability.

Create an `initDB.js` file and add the following code to it.

```Javascript
const { connect, connection } = require('mongoose');
const { config } = require('dotenv'); 

/*const {___} = require(<package>) is called Destructuring. 
This makes our code a lot more cleaner.
*/

/*We'll use module.exports since we want to import this file in our server.js*/

module.exports = () => {
 config(); //invoking the dotenv config here
 const uri = process.env.DB_URI;

 connect(uri, {
		dbName: process.env.DB_NAME,
		user: process.env.DB_USER,
		pass: process.env.DB_PASS,
		useNewUrlParser: true,
		useUnifiedTopology: true,
		useFindAndModify: false,
		useCreateIndex: true
	})
		.then(() => {
			console.log('Connection estabislished with MongoDB');
		})
		.catch(error => console.error(error.message));
}
```

In the code above we've used the `connect` method that Mongoose provides us.
You can read more about mongoose [here]('https://www.npmjs.com/package/mongoose').
 
We pass two parameters to this method. 


```Javascript
connect('<connection_string>', { options });
```

```Javascript
        dbName: process.env.DB_NAME,
		user: process.env.DB_USER,
		pass: process.env.DB_PASS,
```

We can pass our `dbName` `user` and `pass` as options rather than adding them to our connection string.

```Javascript
	    useNewUrlParser: true,
		useUnifiedTopology: true,
		useFindAndModify: false,
		useCreateIndex: true
```

The options above are passed to avoid any `Deprecation warnings`. Think of them as nothing more than boilerplate code for now.

Since the connect method returns a promise (Read more about promises [here](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise)), we can use `.then()` method to handle our promise and the `.catch()` method to handle any errors we may encounter.


In our `.then()` method block , we use a callback function to `console.log()` that we've connected to MongoDB.

In our `.catch()` method block , we'll fire another callback function that gives us the `error` parameter. We'll log this into our console.
```Javascript
console.error(error.message)
```
We're almost done here. Let's require the `initDB.js` file in `server.js`.

```Javascript
//Initialize DB

require('./initDB')();
```

Run `npm start` in your terminal and you should see ,

```Bash
> npm start
Server started on PORT : 5000
Connection established with MongoDB
```

## Adding Mongoose Lifecycle methods

Mongoose provides us with the `connection` method to interface with the MongoDB connection at different phases

1. Connected
2. Disconnected
3. Error

Let's log different messages to the console based on what part of the cycle , our mongoDB connection is at.

Add the following code to initDB.js after the catch block.

```Javascript
	connection.on('connected', () => {
		console.log('Mongoose connected to DB Cluster');
	})

	connection.on('error', (error) => {
		console.error(error.message);
	})

	connection.on('disconnected', () => {
		console.log('Mongoose Disconnected');
	})
```

On restarting the Express server , we can now see 
```Bash
> npm start
Server started on PORT : 5000
Connection established with MongoDB 
Mongoose connected to DB Cluster
```

However , when you stop the server , you'll not see the *disconnected* message. This happens because we're abruptly stopping the express server at the same time. 

Add the following code 

```Javascript
	process.on('SIGINT', () => {
		connection.close(() => {
			console.log('Mongoose connection closed on Application Timeout');
			process.exit(0);
		})

```
Now you'll see the *Mongoose connection closed on Application Timeout* message when you stop the server.

```Javascript
process.on('SIGINT' , () => {..})
```
The above code is used to interface with the server at exactly the point when it is shut down.

With this you have now successfully connected to MongoDB from  your express server using mongoose!

## Defining a User Schema 📚

In your project directory , create a folder called models. We'll define any and all models for our Collection in this folder. 

Let's also create a file called `Users.model.js`.

To this file add the following code

```Javascript
const mongoose = require('mongoose');
const Schema = mongoose.Schema;


const UserSchema = new Schema({
    email: {
        type: String,
        unique: true,
        lowercase: true,
        required: true
    },
    username: {
        type: String,
        unique: true,
        min: 6,
        max: 15
    },
    password: {
        type: String,
        required: true,
        min: 6,
        max: 1024
    },
    role: {
        type: String,
        enum: ['user', 'admin'],
        required: true,
        default: 'user'
    },
    date: {
        type: Date,
        default: Date.now()
    }
    // orders: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Order' }]
})

module.exports = mongoose.model('User', UserSchema);

```

In the code above , we `require('mongoose')` and initialise the Schema method which is part of the Mongoose package to our own Schema constant.

```Javascript
const Schema = mongoose.Schema
```

Now with the help of `Schema` , we can define the structure of what our User's data should look like.

The object defined inside the Schema is self-explanatory. 
You can read about the different options you can pass in as part of the object [here](https://mongoosejs.com/docs/guide.html).

You can see that I have commented out one particular line of code in the definition. Although it is not important to discuss it now , we will be using it to define a foreign key to a different collection.

Finally we'll use the `mongoose.model('<name_of_model>',model_Schema)` to "package/model" our mongoose schema and export it using `module.exports`.

With this , we've learnt how to ,
1. Connect to MongoDB
2. Define a Schema

That's all for part 2 folks! 🌻

In the next part , we'll set up our API's Routes and set up Validation Schemas to validate data that is posted to our server. 👨🏻‍💻