A Comprehensive MongoDB Tutorial for Beginners
Get up and running with MongoDB & Mongoose by creating a Node.js app
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 ismongodb-org-4.4.list
inside thesources.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
- Install Docker on Ubuntu or Mac or Windows.
- Pull the MongoDB image from Docker Hub.
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 Clusters
tab. - 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
.
- 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 underSECURITY
.
- To change or add IP addresses select the
Network Access
tab underSECURITY
. - Assuming you’re not storing sensitive data, click the
Allow Access from Anywhere
button, leave it as is and selectAdd IP Address
. Or see below if you do not see that option. - Create a Database User with a username and password. Click
Create User
.
- To allow your app access to MongoDB Atlas, enter
0.0.0.0/0
in theIP Address
field andAdd Entry
. - IPs can be added or changed by clicking the
Network Access
tab underSECURITY
- Choose a connection method by selecting the second option
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.
Close
.- Create a database by clicking on
COLLECTIONS
. Your databases and collection will appear here once you've created them.
- Select
Add My Own Data
and give your database and collection a name - You should see the database and collection you’ve created.
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.
If using MongoDB Atlas paste the connection string obtained from clicking the CONNECT
tab in your cluster. Remember to replace <password>
.
- You should see the
admin
,local
&config
databases in addition to any others you created.
Connecting our application
Initial Setup
Our app will have the following 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.
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:
Remove the other .jade
files.
Also copy the following JavaScript and CSS content into public/javascripts/client.js
& public/stylesheets/style.css
respectively.
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:
Import and instantiate the routes in app.js
(lines 5
& 19
).
We can view the front-end for our app by running the command below, and navigating to http://localhost:3000/.
npm start
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.
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 of0
.
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.
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
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.
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
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.
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" }
The GET
method for a book is defined from line 56
–68
. 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
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.
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.
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:
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