loading...
Cover image for Connecting to MongoDB using Mongoose 🌳

Connecting to MongoDB using Mongoose 🌳

aritik profile image Ambadi Ritik ・8 min read

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

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

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

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.

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.

Go back to your terminal and run npm start to startup your server and you should see the following message.

> npm start
Server started on PORT : 5000

Creating a MongoDB Cluster 🌈

Let's head to 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

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

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

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

Let's connect to MongoDB using their native drivers.

Alt Text

Copy the connection string.

Alt Text

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.

require('dotenv').config();

To use an environment variable ,

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.

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.

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.

We pass two parameters to this method.

connect('<connection_string>', { options });
        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.

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

console.error(error.message)

We're almost done here. Let's require the initDB.js file in server.js.

//Initialize DB

require('./initDB')();

Run npm start in your terminal and you should see ,

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

    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

> 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

    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.

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

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.

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.

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. 👨🏻‍💻

Posted on by:

aritik profile

Ambadi Ritik

@aritik

I love building things. I love writing CSS. Currently building applications on the PERN stack. Exploring Data Science 🌈

Discussion

markdown guide
 

Very nice explanation! 🙂