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
1C:\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
1mkdir mongodata2cd mongodata3
4mkdir 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
1mongod --dbpath .\data\
If we see an output with
1...22018-12-21T09:01:21.883+0200 I NETWORK [initandlisten] waiting for connections on port 2701732018-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 }42018-12-21T09:01:21.928+0200 I INDEX [LogicalSessionCacheRefresh] building index using bulk method; build may temporarily use up to 500 megabytes of RAM52018-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
1mongo
# Connect to a Database
Next we need to connect to our database to access data, we can do this from the Mongo Shell with
1use mongodata
If successful we will see the output
1switched to db mongodata
# Insert Data
Next we can try to insert an element with the following
1db.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
1newstuff = [{"name":"Nabeel Valley","comment":"Hello Nabeel. Weather good today"},{"name":"Kefentse Mathibe","comment":"I'm OK!"}]2db.comments.insert(newstuff)
# View Data
We can view our inserted data with
1db.comments.find().pretty()
Which will output the following
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" : 711}
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
1require('dotenv').config()2
3// configure express4const express = require('express')5const app = express()6const port = process.env.PORT || 80807const bodyParser = require('body-parser')
# Configure the Database
I’m using monk to easily interface with our database
1const mongo = require('mongodb')2const mongo = require('mongodb')3const monk = require('monk')4const mongoEndpoint = process.env.MONGO_ENDPOINT || 'mongo:27017/mongodata'5const 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
1MONGO_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
1app.use(bodyParser.json())2app.use(bodyParser.urlencoded())3app.use(express.static('public'))4
5app.use((req, res, next) => {6 req.db = db7 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
1app.get('/comments', function (req, res) {2 let db = req.db3 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 <input6 type="text"7 class="form-control"8 id="nameInput"9 placeholder="John Doe"10 value=""11 required12 name="name"13 />14 </div>15 <div class="col-lg-12 mb-3">16 <label for="commentInput">Comment</label>17 <input18 type="text"19 class="form-control"20 id="commentInput"21 placeholder=""22 value=""23 required24 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
1app.post('/submit', (req, res) => {2 // Set our internal DB variable3 let db = req.db4
5 // Set our collection6 let collection = db.get('comments')7
8 // Submit to the DB9 collection.insert(req.body, function (err, doc) {10 if (err) {11 // If it failed, return error12 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 element19 console.log(doc._id)20 // And forward to success page21 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
1docker build -t <USERNAME>/comments-app2docker 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
1kubectl create -f express.yaml2kubectl 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
1apiVersion: extensions/v1beta12kind: Deployment3metadata:4 annotations:5 deployment.kubernetes.io/revision: '1'6 labels:7 app: comments-app8 name: comments-app9spec:10 replicas: 111 selector:12 matchLabels:13 app: comments-app14 template:15 metadata:16 labels:17 app: comments-app18 name: comments-app19 spec:20 containers:21 - image: nabeelvalley/comments-app22 imagePullPolicy: Always23 name: comments-app24
25---26apiVersion: v127kind: Service28metadata:29 labels:30 app: comments-app31 name: comments-app32spec:33 ports:34 - name: tcp-8080-8080-comments-app35 nodePort: 3001636 port: 808037 protocol: TCP38 targetPort: 808039 selector:40 app: comments-app41 type: LoadBalancer
mongo.yaml
1apiVersion: v12kind: Service3metadata:4 name: mongo5 labels:6 run: mongo7spec:8 ports:9 - port: 2701710 targetPort: 2701711 protocol: TCP12 selector:13 run: mongo14
15---16apiVersion: extensions/v1beta117kind: Deployment18metadata:19 name: mongo20spec:21 template:22 metadata:23 labels:24 run: mongo25 spec:26 containers:27 - name: mongo28 image: mongo29 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