Overview

This tutorial is for beginners who know a bit of JavaScript and want to get started using MongoDB.

We’ll create a NodeJS app using the Express framework with MongoDB serving as our database. It’s a simple personal library, in which we’ll be able to:

  • View a list of books.
  • Add a book.
  • Add comments about a book.
  • View a specific book and the comments associated with it.
  • Delete a book.
  • Delete all books from the library.

This tutorial has a section outlining various MongoDB installation options. If you already have it installed, skip to the Connecting our application section.

Let’s take care of the back-end first. This section assumes that you already have NodeJS installed but not MongoDB. If not, here’s a quick guide. There are a number of ways to run and use MongoDB. Review and choose the most convenient one for you.

In terms of ease of setup, MongoDB Atlas is the quickest way to get up & running, followed by Docker, if already installed, or else just use the quick install method unless you’re using Windows or Mac. Honestly not sure why they’re in the order that they are below 🤷🏾‍♂️️.

Whether you’re using an installed or hosted MongoDB version, I’d recommend installing a MongoDB GUI which provides a user interface for a MongoDB database. With a GUI, we can visualize data and edit queries without the use of shell commands. Refer to the MongoDB GUI section below

MongoDB Installation

1. Quick Install (Ubuntu 20.04)

  • Import the public GPG key for the latest stable version of MongoDB by running the following command.
curl -fsSL https://www.mongodb.org/static/pgp/server-4.4.asc | sudo apt-key add -
  • Create a sources.list file for MongoDB which lists it as an active source of APT data. In this case, the file is mongodb-org-4.4.list inside the sources.list.d folder.
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/4.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.4.list
  • Update the local package index so APT knows where to find the mongodb-org package, and install MongoDB.
sudo apt update; sudo apt install mongodb-org -y
  • Start the MongoDB service:
sudo systemctl start mongod.service
  • Check MongoDB service’s status with the following command:
sudo systemctl status mongod
  • Output:
● mongod.service - MongoDB Database Server
Loaded: loaded (/lib/systemd/system/mongod.service; enabled; vendor preset: enabled)
Active: active (running) since Sun 2021-07-04 13:35:51 CAT; 2 days ago
Docs: https://docs.mongodb.org/manual
Main PID: 1251 (mongod)
Memory: 78.6M.
CGroup: /system.slice/mongod.service
└─1251 /usr/bin/mongod --config /etc/mongod.conf

To further secure your MongoDB installation (Optional).

2. Using Docker

docker pull mongo
  • Run MongoDB instance and assign the container a name using the— name option.
docker run — name mongodb -p 27017:27017 -d mongo
  • To run MongoDB on another port other than 27017 e.g. 37017 :
docker run — name mongodb -p 37017:27017 -d mongo
  • Output will be a string indicating a container ID:
470d71083f835c…
  • To check that Docker is running:
docker ps
  • Output:
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS       NAMES 470d71083f83   mongo     "docker-entrypoint.s…"   2 minutes ago   Up 2 minutes   27017/tcp   mongodb
  • If the container stops for some reason after you’ve created it, simply run:
docker start mongodb
  • To stop the container all together:
docker stop mongodb

3. MongoDB Atlas

  • Alternatively, you can use MongoDB hosted in the cloud using MongoDB Atlas.
  • Create an account. A free tier is available.
  • After signing up, you’ll be asked to name your organization and project and preferred language.
  • Create a cluster on the Shared Clusterstab.
  • For the purpose of testing, select a region closer to your own geographic location and leave everything else as is, maybe changing the Cluster Name.
MongoDB Atlas setup
MongoDB Atlas cluster name
  • Select Create Cluster at the bottom of the page.
  • Wait for the cluster to be created, then click on the CONNECT tab below your cluster name. This part may vary depending on your browser. You may be asked to create a database user first.
  • To create or add a new database user, click the Database Access tab on the left side of the screen under SECURITY .
MongoDB Atlas connect
  • To change or add IP addresses select the Network Access tab under SECURITY .
  • Assuming you’re not storing sensitive data, click the Allow Access from Anywhere button, leave it as is and select Add IP Address. Or see below if you do not see that option.
  • Create a Database User with a username and password. Click Create User .
Create database user using a username and password
Select My Local Environment
  • To allow your app access to MongoDB Atlas, enter 0.0.0.0/0 in the IP Address field and Add Entry .
  • IPs can be added or changed by clicking the Network Access tab under SECURITY
Allow app to access MongoDB Atlas from anywhere.
  • Choose a connection method by selecting the second option Connect your application.
MongoDB Atlas Connect your application
  • Copy the connection string on step 2 to a secure location. Replace <password> with the password you used when creating a database user. We’ll use it as an environmental variable in our app.
MongoDB Atlas connection string
  • Close.
  • Create a database by clicking on COLLECTIONS. Your databases and collection will appear here once you've created them.
MongoDB Atlas get connection string
  • Select Add My Own Data and give your database and collection a name
  • You should see the database and collection you’ve created.
MongoDB Atlas create, view database and collection

MongoDB GUI

There are a number of options for a MongoDB GUI that increase the ease of use of MongoDB and are compatible with the any of the 3 methods described above e.g. MongoDB Compass, Robo3T, Studio3T.

In this tutorial we’ll use MongoDB Compass.

Download your system’s version and open it once installed.

If you’re using a local MongoDB instance, ensure that it is running, then selectFill in connection fields individually. Unless the port specified during setup is not 27017, leave the settings as they are.

MongoDB Compass settings

If using MongoDB Atlas paste the connection string obtained from clicking the CONNECT tab in your cluster. Remember to replace <password>.

MongoDB Atlas plus MongoDB Compass
  • You should see the admin, local & config databases in addition to any others you created.
MongoDB Compass connected

Connecting our application

Initial Setup

Our app will have the following folder structure:

App folder structure

Initialize the app by first creating an empty directory for the app:

mkdir personal-library

Change into the created directory:

cd personal-library

Generate a barebone app using express-generator :

npx express-generator

Replace package.json ’s contents with the following, and install the dependencies.

package.json
npm i

Create a.env file in the root of your app. Use the appropriate variable name depending on what type of MongoDB instance you’re using, MONGO_ATLAS if using MongoDB Atlas. Get the connection string from your MongoDB Atlas account. Replace <password>.

Use MONGO_LOCAL if using a local instance of MongoDB. The format is mongodb://localhost:27017/<database>. Replace <database> with the name of your database. In this case library is used.

Delete the one not in use.

If the database doesn't exist, it will be created.

The file should resemble with either one of the 2 lines:

If you plan on committing this project, create a .gitignore file and include .env inside it. Here is a useful repo that has .gitignore files for different programming languages and frameworks.

Create a .gitignore file with the necessary specifications for NodeJS directly by running the following command in the root of your app.

wget -O .gitignore https://raw.githubusercontent.com/github/gitignore/master/Node.gitignore

Or if you have to do it by manually, in the root of your app:

touch .gitignore

Copy the contents of this file into it.

Replace app.js with the following initial setup:

Rename index.jade file in the views folder to index.html file and replace the contents with the following:

views/index.html

Remove the other .jade files.

Also copy the following JavaScript and CSS content into public/javascripts/client.js & public/stylesheets/style.css respectively.

public/javascripts/client.js
public/stylesheets/style.css

The client-side JavaScript is glossed over as it is not the focus of this tutorial. However you can look up the jQuery methods used in the documentation.

Inside the routes folder create a file api.js which will hold our routes.
We won't need the other files, so we can delete them.
With these routes, we'll be able to:

  • View all books 👉🏾️GET request to /api/books .
  • Add a book 👉🏾️POST request to /api/books .
  • Delete all books 👉🏾️DELETE request to /api/books .
  • View a book 👉🏾️GET request to /api/books/:id .
  • Add comments on an existing book 👉🏾️POST request to /api/books/:id .
  • Delete a book 👉🏾️DELETE request to /api/books/:id .

Our initial api.js file will resemble:

routes/api.js

Import and instantiate the routes in app.js(lines 5 & 19).

app.js

We can view the front-end for our app by running the command below, and navigating to http://localhost:3000/.

npm start
Front-end, personal library app

Using mongoose

We’ll be using mongoose, a module that implements Object Document Mapping methods for MongoDB.

It simplifies CRUD (Create, read, update and delete) operations , document validation and other features without much concern for how the documents relate to their data sources.

Set up the app to use mongoose by importing it into app.js. We’ll then use it to establish a connection to the database.

Uncomment the appropriate line, either 12 or 13 , defining the variable DB depending on your MongoDB setup.

app.js

Note dotenv module import on line 2. This allows us to load environmental variables from the.env file we created earlier. We can access these variables throughout the app via process.env as process.env.VARIABLE_NAME. In this case our variable is either MONGO_LOCAL or MONGO_ATLAS . Using environmental variables allows to set up different configuration options for different environments.

Creating and using models

Next, we’ll create a folder to hold the apps models. A model is an object that we’ll use to send data to the database. In this case we only have 1 model — Book which has these properties:

  • title represented by a string.
  • comments represented by an array of strings.
  • commentcount represented by a number, with an initial value of 0.

Create the models directory.

mkdir models

Create book.js file inside the models folder.

touch models/book.js

We’ll create a schema for a book. A schema in mongoose maps to a MongoDB collection and defines the shape of the documents stored in that collection. The shape of the documents refers to the properties defined within a schema (title, comments & commentcount).

To use our schema definition, we need to convert bookSchema into a model (line 10), which is a fancy constructor compiled from a schema definition. An instance of a model is created when we add a book. This model instance is a document, which is then stored in a collection within a database.

Line 2 is an example of object destructuring which makes it possible to unpack values from arrays, or properties from objects, into distinct variables.

models/book.js

Defining routes

We can use our Book model in the routes to perform CRUD functions. Import it into routes/api.js . The first route we’ll define is to get the list of books.

View all books

routes/api.js

We use mongoose ’s Model.find() method. Passing an empty object {} as an argument to the find method returns all the documents. An error is thrown and logged to the console if the connection is unsuccessful. Curly braces ({}) can be omitted from if statements if there’s only 1 statement, in this case the error we throw.

If we navigate to http://localhost:3000/api/books in the browser, we get an empty array as our collection is still empty.

Add a book

To add a book to the library, our POST method checks whether or not the request body has a property title and is a non-empty string, otherwise an error is thrown indicating a required field is missing. An new instance of the Book model is created and the saved document is returned as JSON

Edit the POST method for the /api/books route.

routes/api.

From our running front-end, lets add a book, e.g. The Alchemist. An array with the added book is returned when we visit http://localhost:3000/api/books

JSON Viewer in a browser

The returned book object has 2 new properties attached to it _id and __v . _id is a unique ID automatically assigned by MongoDB to every document stored within a collection. We’ll use this property to interact with a book in other routes.

__v is a property set on each document when first created by mongoose. This key’s value contains the internal revision of the document.

The same is reflected in MongoDB Compass.

MongoDB Compass database.collection with document

Go ahead and add more books to the database. You should see a list of books both on front-end and MongoDB Compass.

View / Get a book

We retrieve a book when a GET request is made to /api/books/{bookId} where bookId is a placeholder for a dynamic URL. In the route /api/books/:id, the :id segment of the URL is a route parameter. It is used to capture values specified at that position in the URL. In this case, a book’s id. The captured values are stored in the req.params object, with the name of the route parameter (id) as the key.

When we click on the first book in the list, the book title and its id are displayed. In this example, The Alchemist with an id 60e79c75e3280d4c52fce456 will be captured in the URL and accessed via req.params.id .

Route path: /api/books/:idRequest URL: http://localhost:3000/api/books/60e79c75e3280d4c52fce456req.params: { "id": "60e79c75e3280d4c52fce456" }
routes/api.js GET /api/books/:id

The GET method for a book is defined from line 5668 . We get the book’s ID from the req.params object. If no ID is found we throw an error. We use mongoose’s Model.findById() method, passing in the ID as an argument. The orFail() method is attached to the query and throws an error if no matching document is found i.e. an invalid ID was used in the query. The matched book is the returned as JSON which is parsed by the client-side JavaScript and the book and it’s comments are displayed.

We catch mongoose’s CastError which is returned when mongoose fails to cast the filter (id) to our schema. This keeps the app running even after we encounter this error.

Add a comment

Clicking on a book in the list opens up a form that allows us to add comments to that book. On adding an actual comment, the form data is sent to the /api/books/:id . Form data is captured in the req.body object. E.g. Adding a comment The Alchemist with ID 60e79c75e3280d4c52fce456 :

Route path: /api/books/:idRequest URL: http://localhost:3000/api/books/60e79c75e3280d4c52fce456req.params: { "id": "60e79c75e3280d4c52fce456" }req.body: { "comment": "Great book" }

Let’s update the POST method of the /api/books/:id route

routes/api.js POST /api/books/:id

We get the book’s ID and comment from req.params and req.body objects respectively. An error is thrown if the comment is empty. We use mongoose’s Model.findOneAndUpdate() method, passing in the id and comment and other options as arguments.

The first argument is an object with the _id to find. MongoDB's $push operator appends a specified value to an array in a document with that particular _id. In this case, the newly added comment to the comments array. The $inc operator increments a field by a specified value. Here, we increment commentcount and versionKey (__v) by 1 each time we add a comment. We add the new: true option so as to return the document with modifications made on the update.

Personal library with comments
MongoDB Compass

Delete a book

A book is deleted when we click on the Delete book button. The book’s ID is sent with a DELETE request to /api/books/:id . The book’s ID is parsed from the req.params object and passed in as an argument in the Model.findByIdAndRemove() method. An error is thrown if the ID is invalid or non-existent, otherwise a message indicating successful deletion is returned.

routes/api.js DELETE api/books/:id

Delete all books

Finally, we can delete all the books in the library collection by sending a DELETE request to /api/books and executing the Model.deleteMany() method without any arguments.

The complete api.js file will be:

routes/api.js

Wrapping up

Our app is now complete and we can perform the functions that we set out to create at the start of this tutorial.

This is a primer in MongoDB and some of the methods associated with common operations that you would execute on a database. Feel free to tweak the app however you want, experimenting with more MongoDB and mongoose methods.

Let me know your thoughts in the comments. Happy hacking 👍🏾️.

More content at plainenglish.io

--

--