Express Application with MongoDB

Build an Express Application that uses MongoDB and Docker

Updated: 03 September 2023

Built with tons of help from:

Setting up Mongo

# Adding Mongo to your PATH

If you have just downloaded and installed MongoDB, it may not be defined as a system variable in your PATH, you can do that finding the Mongo installation directory and adding this as a system environment variable, the directory should be like the following for Windows

1
C:\Program Files\MongoDB\Server\4.0\bin\

This will give us access to both mongo and monod commands

# Create a Data Directory

Next we will need a place to store our data, we can create this directory anywhere we want, in this case I’ll make it inside of my app directory

Terminal window
1
mkdir mongodata
2
cd mongodata
3
4
mkdir data

# Running the DB Server

Next we can run our database server with the following command inside of our mongo directory that we just created

Terminal window
1
mongod --dbpath .\data\

If we see an output with

1
...
2
2018-12-21T09:01:21.883+0200 I NETWORK [initandlisten] waiting for connections on port 27017
3
2018-12-21T09:01:21.928+0200 I INDEX [LogicalSessionCacheRefresh] build index on: config.system.sessions properties: { v: 2, key: { lastUse: 1 }, name: "lsidTTLIndex", ns: "config.system.sessions", expireAfterSeconds: 1800 }
4
2018-12-21T09:01:21.928+0200 I INDEX [LogicalSessionCacheRefresh] building index using bulk method; build may temporarily use up to 500 megabytes of RAM
5
2018-12-21T09:01:21.936+0200 I INDEX [LogicalSessionCacheRefresh] build index done. scanned 0 total records. 0 secs

We know that the server is running

# Creating and Viewing Elements

In a new terminal window we open the Mongo Shell with

Terminal window
1
mongo

# Connect to a Database

Next we need to connect to our database to access data, we can do this from the Mongo Shell with

Terminal window
1
use mongodata

If successful we will see the output

Terminal window
1
switched to db mongodata

# Insert Data

Next we can try to insert an element with the following

Terminal window
1
db.comments.insert({"name":"myuser", "comment":"Hell World!"})

Mongo uses BSON (basically JSON with some sprinkles) for data storage

We can also insert data as follows

Terminal window
1
newstuff = [{"name":"Nabeel Valley","comment":"Hello Nabeel. Weather good today"},{"name":"Kefentse Mathibe","comment":"I'm OK!"}]
2
db.comments.insert(newstuff)

# View Data

We can view our inserted data with

Terminal window
1
db.comments.find().pretty()

Which will output the following

Terminal window
1
{
2
"_id" : ObjectId("5c1c93222712ad7e454a01a2"),
3
"username" : "myuser",
4
"email" : "me@email.com"
5
}
6
{ "_id" : ObjectId("5c1c94562712ad7e454a01a3"), "name" : "nabeel" }
7
{
8
"_id" : ObjectId("5c1c94562712ad7e454a01a4"),
9
"email" : "unknown@email.com",
10
"age" : 7
11
}

Building the Express App

This can all be found in the server.js file

The Express app will do a few things:

  • Serve the necessary static files for the app
  • Get and Insert data into Mongo
  • Build and send the Mongo content to the frontend

# Importing the Necessary Libraries

I’m making use of the following libraries to

  • Read environmental variables
  • Create my server
  • Parse JSON body from the create form
1
require('dotenv').config()
2
3
// configure express
4
const express = require('express')
5
const app = express()
6
const port = process.env.PORT || 8080
7
const bodyParser = require('body-parser')

# Configure the Database

I’m using monk to easily interface with our database

1
const mongo = require('mongodb')
2
const mongo = require('mongodb')
3
const monk = require('monk')
4
const mongoEndpoint = process.env.MONGO_ENDPOINT || 'mongo:27017/mongodata'
5
const db = monk(mongoEndpoint)

I’ve used the default value of mongo:27017 for when the application is run in k8s in order to interface with a mongo instance more easily. If running locally we can set the MONGO_ENDPOINT in a .env file in the project root directory as follows

1
MONGO_ENDPOINT=localhost:27017

# Middleware

Configure express middleware for the following

  • Use static files
  • Parse JSON and Form data from request
  • Make DB accessible to the app
1
app.use(bodyParser.json())
2
app.use(bodyParser.urlencoded())
3
app.use(express.static('public'))
4
5
app.use((req, res, next) => {
6
req.db = db
7
next()
8
})

# View Comments

Next I define a /comments that will retrieve content from the comments collection, render it with the base.card and base.content functions and send that as a response

1
app.get('/comments', function (req, res) {
2
let db = req.db
3
let collection = db.get('comments')
4
collection.find({}, {}, function (e, docs) {
5
const base = require('./base')
6
7
let content = ''
8
docs.reverse().forEach((comment) => {
9
content += base.card(comment.name, comment.comment, comment._id)
10
})
11
content = base.content(content)
12
13
res.send(content)
14
})
15
})

# Creeate Comment

To create a comment I’ve used a simple form in the frontend, which can be seen below

1
<form action="/submit" method="post">
2
<div class="form-row">
3
<div class="col-lg-12 mb-3">
4
<label for="nameInput">Full Name</label>
5
<input
6
type="text"
7
class="form-control"
8
id="nameInput"
9
placeholder="John Doe"
10
value=""
11
required
12
name="name"
13
/>
14
</div>
15
<div class="col-lg-12 mb-3">
16
<label for="commentInput">Comment</label>
17
<input
18
type="text"
19
class="form-control"
20
id="commentInput"
21
placeholder=""
22
value=""
23
required
24
name="comment"
25
/>
26
</div>
27
</div>
28
<button class="btn btn-primary" type="submit">Submit form</button>
29
</form>

And an express route to handle the post and insert the new comment to the database

1
app.post('/submit', (req, res) => {
2
// Set our internal DB variable
3
let db = req.db
4
5
// Set our collection
6
let collection = db.get('comments')
7
8
// Submit to the DB
9
collection.insert(req.body, function (err, doc) {
10
if (err) {
11
// If it failed, return error
12
const base = require('./base')
13
const content = base.content(
14
'<h1>There was an error sending your content, please try again<h2>'
15
)
16
res.send(content)
17
} else {
18
// get id of inserted element
19
console.log(doc._id)
20
// And forward to success page
21
res.redirect(`comments`)
22
// res.redirect(`comments/${doc._id}`)
23
}
24
})
25
})

Deploy on k8s

Once we are done we can push this as a Docker image and deploy it on a Kubernetes Cluster as follows

# Building the Image

From the application directory run

Terminal window
1
docker build -t <USERNAME>/comments-app
2
docker push

# Deploying on Kubernetes

Once logged into a kubernetes cluster we can make use of the express.yaml to deploy the express app, and the mongo.yaml file to deploy Mongo

1
kubectl create -f express.yaml
2
kubectl create -f mongo.yaml

This will create a deployment as well as a service for both the Express App and Mongo. The deployment configs are as follows

express.yaml

1
apiVersion: extensions/v1beta1
2
kind: Deployment
3
metadata:
4
annotations:
5
deployment.kubernetes.io/revision: '1'
6
labels:
7
app: comments-app
8
name: comments-app
9
spec:
10
replicas: 1
11
selector:
12
matchLabels:
13
app: comments-app
14
template:
15
metadata:
16
labels:
17
app: comments-app
18
name: comments-app
19
spec:
20
containers:
21
- image: nabeelvalley/comments-app
22
imagePullPolicy: Always
23
name: comments-app
24
25
---
26
apiVersion: v1
27
kind: Service
28
metadata:
29
labels:
30
app: comments-app
31
name: comments-app
32
spec:
33
ports:
34
- name: tcp-8080-8080-comments-app
35
nodePort: 30016
36
port: 8080
37
protocol: TCP
38
targetPort: 8080
39
selector:
40
app: comments-app
41
type: LoadBalancer

mongo.yaml

1
apiVersion: v1
2
kind: Service
3
metadata:
4
name: mongo
5
labels:
6
run: mongo
7
spec:
8
ports:
9
- port: 27017
10
targetPort: 27017
11
protocol: TCP
12
selector:
13
run: mongo
14
15
---
16
apiVersion: extensions/v1beta1
17
kind: Deployment
18
metadata:
19
name: mongo
20
spec:
21
template:
22
metadata:
23
labels:
24
run: mongo
25
spec:
26
containers:
27
- name: mongo
28
image: mongo
29
ports:
30
- containerPort: 27017

Running Locally

If you’d like to run this application on a local Kubernetes cluster, take a look at the page on Deploying an Express App that Uses Mongo on k8s Locally