Introduction to Mongodb for Node.js developers

What is Mongodb?

Mongodb is a document based database which is built to be distributed and highly available. All the documents stored in the Mongodb are JSON like format which makes Node.js a perfect programming language to choose. Mongodb is the front runner of the Nosql movement, it is the most popular Nosql database. It has a flexible Schema, meaning the documents that are stored in a collection can have different structures. Ok what is the document and what is the collection?

The Database in Nosql world is similar to schemas in relation database world, Collection is similar to tables and a document is a row in a table. So in summary, Database is a collection of collections, and collection is a collection of documents in Mongodb.

If you are coming from the relational database world and you are trying to design a Schema for your application using Mongodb, You need to put some of the normalization rules aside. Mongodb offers you more than just primitive RDBMS data types. You can have nested objects and arrays in your collections.
Imagine you want to design a system that tracks your users interests and locations. In the RDBMS world, you need to have at least 3 tables to accomplish the requirements (Users, interests and Locations). And if you want to retrieve a user’s information, you need to join all those tables together. With Mongodb, we can have all the information in one document. Can you already see how simple it is?

{
  Name: "Farzad",
  LastName: "Aziminia",
  Interests: ["development", "music", "cycling"],
  Locations: [{city: "Irvine", current: true}, {city: "dana point"}],
  Occupation: "Software Engineer",  
}

As you can see All of your user’s information can reside within one JSON document. There’s no need of nasty joins. The inserts and queries are blazing fast compared to traditional relational databases.
Interestingly, Mongodb's query language is also JSON like. Imagine you want to query all the users who are named “Farzad”. Your query will look like this: {Name: "Farzad"}. Amazing! Isn't it?

Run mongodb inside docker

docker run -d --name mongodb -e MONGO_INITDB_ROOT_USERNAME=root -e MONGO_INITDB_ROOT_PASSWORD=password -p 27017:27017 mongo

The above command will run a mongodb instance inside a docker container. We set an initial username and password to root/password and bind the port 27017 of our host to the docker’s 27017 port. Now we should be able to connect to our mongodb.

To connect to Mongodb, we will use the native Mongodb library, there are other good alternative line mongoose, but for this tutorial, we will use the native library.

Create Connection

To start, let's install mongod library, here is how your package.json should look like:

{
 "name": "mongodb",
 "version": "1.0.0",
 "description": "",
 "main": "index.js",
 "scripts": {
   "test": "echo \"Error: no test specified\" && exit 1"
 },
 "author": "",
 "license": "ISC",
 "dependencies": {
   "mongodb": "^4.0.0"
 }
}

Ok, after you create package.json file and paste the above content to it, you can do npm install.

Now, let’s create a file called mongodb.js

const mongo = require('mongodb').MongoClient

const PARAMS = {
   useNewUrlParser: true,
   useUnifiedTopology: true,
};
const URI = 'mongodb://root:password@localhost:27017'

let connection

const connectMongo = async () => {
   connection = await mongo.connect(URI, PARAMS);
   console.log('Mongodb Connected');
}

module.exports = { connectMongo }

There is a lot going on. Let's get to it. In the first line, from the Mongodb package, I imported MongoClient and assigned it to a constant variable called mongo. We're gonna skip to 7, at line 7 we constructed our Mongodb connection string. Our docker image is mapped to our localhost port 27017 and the initial username password is root and password, line 13 we created a function to kick off the connection process by passing URI and the extra configuration. You can read more about the configuration here:
https://mongodb.github.io/node-mongodb-native/3.2/api/MongoClient.html
And at the last line, we expose our connection function to the other functions. Now lets Create our index.js file

Create our index.js file

const connectMongo = require('./mongodb').connectMongo

const startup = async () => {
   await connectMongo()
}

startup()

Now we can run our index.js file. if everything has been done correctly, you should be able to see the “Mongodb Connected” message.

Now let’s modify our mongodb.js file to expose our connection object

const mongo = require('mongodb').MongoClient

const PARAMS = {
   useNewUrlParser: true,
   useUnifiedTopology: true,
};
const URI = 'mongodb://root:password@localhost:27017'

let connection

const connectMongo = async () => {
   connection = await mongo.connect(URI, PARAMS);
   console.log('Mongodb Connected')
}

const getConnection = () => {
   return connection;
}

module.exports = { connectMongo, getConnection }

Now I’m gonna create a new file called user.js to create the necessary functions that facilitate the communication with Mongodb

const mongo = require('./mongodb');
const DB_NAME = 'users';
const COLLECTION_NAME = 'users';

const getUserCollection = () =>  mongo.getConnection().db(DB_NAME).collection(COLLECTION_NAME)

const createUser = async (user) => {
   return getUserCollection().insertOne(user);
}

const getUsers = async (criteria={}) => {
   return getUserCollection(criteria).find({});
}

const getSingleUser = async (criteria) => {
   return getUserCollection().findOne(criteria);
}

const deleteSingleUser = async (criteria) => {
   return getUserCollection().deleteOne(criteria);
}

const deleteAllUsers = async () => {
   return getUserCollection().deleteMany({})
}

module.exports = {
   deleteAllUsers,
   deleteSingleUser,
   getSingleUser,
   getUsers,
   createUser,
}

At line 6, I created a function to return the users inside users collection, to do that, first we need to point to our database Users, from there we can point to our collection Users. One thing I wanna mention is, unlike relational databases, you don’t need to create a database or collection before you can start inserting records. Mongodb will do all that for you. When you command Mongodb to insert a document into your collection, if the collection does not exist, it will create one for you.

Crud Operations

In order to interact with Mongodb driver, we have to utilize the functions that it designated for us. Mostly the functions do return a Promise. You can find the list of all the functions on the following link. https://mongodb.github.io/node-mongodb-native/3.2/api/Collection.html#find

One thing I wanna mention is, when you are trying to get a list of documents from a collection, the driver returns a Cursor Object. You can iterate through the cursor object differently. In our example I used toArry() method to convert it to an array.
You can read more here https://mongodb.github.io/node-mongodb-native/3.2/api/Cursor.html

Let’s start using our functions. For this example, I’m gonna create 3 users and then start writing some queries and at the end, delete all users.

Let’s start modifying our index.js file

const connectMongo = require('./mongodb').connectMongo
const User = require('./user')

const user1 = {
   name: 'Farzad',
   lastName: 'Aziminia',
   interests: ['Software Engineer', 'Music', 'Cycling'],
   locations: [{ city: 'Irvine', current: true }, { city: 'Dana Point' }],
};

const user2 = {
   name: 'Adam',
   lastName: 'Smith',
   interests: ['Construction Worker', 'Cars', 'Beer'],
   locations: [{ city: 'Los Angeles', current: true }, { city: 'Newport Beach' }],
};

const user3 = {
   name: 'Jack',
   lastName: 'Ryan',
   interests: ['Software Engineer', 'Cars', 'hiking'],
   locations: [{ city: 'Santa Ana' }],
};

const startup = async () => {
   await connectMongo()
   await User.createUser(user1);
   console.log('user1 created');
   await User.createUser(user2);
   console.log('user2 created');
   await User.createUser(user3);
   console.log('user3 created');
   const result = await User.getUsers();
   console.log(JSON.stringify(result));
}

startup()

If you run the code, you should be able to see all the users created in our collection. The result of our getAllUsers query should look like this:

[{"_id":"60f6332a75935a4199c3d615","name":"Farzad","lastName":"Aziminia","interests":["Software Engineer","Music","Cycling"],"locations":[{"city":"Irvine","current":true},{"city":"Dana Point"}]},{"_id":"60f6332a75935a4199c3d616","name":"Adam","lastName":"Smith","interests":["Construction Worker","Cars","Beer"],"locations":[{"city":"Los Angeles","current":true},{"city":"Newport Beach"}]},{"_id":"60f6332a75935a4199c3d617","name":"Jack","lastName":"Ryan","interests":["Software Engineer","Cars","hiking"],"locations":[{"city":"Santa Ana"}]}]

Have you noticed that all of our documents carry a new element called _id? Mongodb creates a unique identifier for every document and attaches it automatically.

Now I’m gonna get all the users that are named “Farzad”.

Note: from now on, I’m just gonna update the function startup, the rest of the index.js file will be the same as above

const startup = async () => {
   await connectMongo()
   const result = await User.getSingleUser({name: 'Farzad'});
   console.log(result);
}

The query that gets passed to the driver is { name: "Farzad" }. This is how you have to write your queries against the Mongodb. If I wanna get all users named jack, my query would look like this, { name: "Jack" }. Ok now let's get all the users that either name Farzad Or Jack.

const startup = async () => {
   await connectMongo()
   const result = await User.getUsers({$or:[{name: 'Farzad'}, {name: 'Jack'}]});
   console.log(result);
}

I used the $or operator, $or accepts an array of expressions and performs logical OR operation. In our case, we are saying return all the users that are named Farzad OR Jack.
The other thing I wanted to mention is: If you look at Users.js file, getUsers function uses find method but getSingleUser uses findOne method in the background. The difference is, find returns an array results but findOne only returns the first document that matches the criteria.
Ok, let's do an array operation. I want to get all the users who are interested in Cars:

​​

const startup = async () => {
   await connectMongo()
   const result = await User.getUsers({interests: 'Cars'});
   console.log(result);
}

Querying arrays is similar to querying a normal object. Now I want to get the list of users who has the current location.

const startup = async () => {
   await connectMongo()
   const result = await User.getUsers({'locations.current':{$exists: true}});
   console.log(result);
}

This is how you can query the nested elements within an object. Now lets delete all users whose name is Jack and last name is Ryan.

const startup = async () => {
   await connectMongo()
   const result = await User.deleteSingleUser({name: 'Jack', lastName: 'Ryan'});
   console.log(result);
}

If you look at the result, you will see it gives you the number of deleted documents:

{ acknowledged: true, deletedCount: 1 }

And if you query the database again, you should see only 2 documents

const startup = async () => {
   await connectMongo()
   const result = await User.getUsers();
   console.log(JSON.stringify(result));
}

Lastly, lets delete all the documents

const startup = async () => {
   await connectMongo()
   const result = await User.deleteAllUsers();
   console.log(result);
   const records = await User.getUsers()
   console.log(records)
}

When you run the application, You should be able to see the following output:

Mongodb Connected
{ acknowledged: true, deletedCount: 2 }
[]

That will conclude our today’s tutorial. Stay tuned for more advance Mongodb tutorials

Conclusion:

Mongodb is a very high performance document based database
The query language of Mongodb is json, that’s why Node.js is a perfect fit since Javascript supports Json natively
Each record inside Mongodb called Document and a collection of documents called Document and within a database you can have N number of Collections

15