MERN to Kubernetes — Part 1

Jake Peterson
7 min readFeb 19, 2021

--

MERN app Express Backend and Mongo Database Setup

Recently I was tasked with standing up an in-house DevOps pipeline and rebuilding an old data driven web app with modern technologies. I came into this assignment with intermediate level knowledge of various programming languages, a basic understanding of DevOps concepts, and a strong ability to self-learn and adapt publicly available guides. This series serves to document my learning as I worked to build a MERN (MongoDB, Express.js, React.js, Node.js) web app, and deploy it to a Kubernetes pod through Gitlab. This is my first attempt at writing a development guide and I welcome any feedback.

This guide is not meant to be a tutorial on Javascript or React and assumes a basic ability level to write code in those languages. I highly recommend Dr. Angela Yu’s “The Complete 2021 Web Development Bootcamp” on Udemy as a great place to start learning full stack web development.

Part 1 of this series sets up the MongoDB and Node/Express backend. For demo purposes the functionality of the app will be a simple grocery shopping list.

**CLI commands are indicated with a $. Copy everything after the $ for the proper command. If you see something between <> replace them with whatever the brackets are prompting you for (i.e replace <project name> including the carrots with My-app or whatever you want to call it).**

For part 1 of this guide you will need an IDE setup, git installed/configured, access to a MongoDB, and Node.js. Below are some links if you need help with those requirements.

IDE: https://code.visualstudio.com/download

git: https://git-scm.com/downloads

MongoDB: https://www.mongodb.com/cloud/atlas

Node.js: https://nodejs.org/en/download/

Setup a MongoDB cloud atlas account

If you already have access to a local MongoDB instance or some other MongoDB server you can skip this step. If you need a MongoDB for this guide follow the below steps to create a free tier MongoDB cloud atlas account. This free tier account is suitable for small scale development and testing but would need to be scaled up for a production load.

Go to https://www.mongodb.com/cloud/atlas/signup and create a free MongoDB atlas account

On the “Lets get your account setup” page set a name for your organization and project. Select Javascript for the preferred language

Select the free shared cluster option on the “Choose a path” page

Select a cloud provider and a region on the “Create a Starter Cluster page”, Make sure you have “M0 Sandbox” with a base price of “Free Forever” selected for your “Cluster Tier”, Click “Create Cluster”

Once the Cluster has finished creating click the “Connect” button. The page will tell you that you need to setup your firewall access. Click the option “Allow Access from Anywhere”

Leave the IP address as 0.0.0.0/0 and click “Add IP Address”

**This configuration allows a connection to the database from any IP address. In any production configuration this would be an insecure setting and you would want to specify IP addresses of your particular environment but since most people do not have static IP addresses this will work for testing/demo purposes.**

In the “Create a Database User” section set your username and password as you desire. Click the “Create a Database User” button. This is the username and password you will use in your MongoDB connection URI down below.

**Some special characters seem to cause problems with passwords in the MongoDB connection URI. Do not use the @ symbol especially.**

Click “Choose a connection method”. On the next screen select “Connect your application”

Copy the connection string into a notepad (or something to keep note of it for later steps) replacing the < password > portion (including the < >) with the password you setup on step 10 and replacing < dbname > portion with mydb (or whatever you want to call the database your grocery data will be insterted into)

It should look something like:

mongodb+srv://mydbUser:mydbpassword@cluster0.abcde.mongodb.net/mydb?retryWrites=true&w=majority

Project Structure

This section explains the overall structure of the project. Do not manually create the folder structure explained here. The proper folders will be created automatically in the steps below. The MERN app will be structured in two parts, the Node/Express Backend and the React.js Frontend. The root directory of the project will house all the files for backend. Inside the root directory of the project there will be a folder called frontend that will house all the files for the React.js frontend. When you get to the step where you initialize the git repository for your project make sure you are running that command from a terminal that is pointed to the root directory.

NodeCreate Node/Express Backend

Generate Node/Express boilerplate

In VS Code (or your chossen IDE), open a terminal, change directory into the directory in which you want to create the project and type:

$ npx express-generator --no-view --git <replace with desired project name>

This will create a boilerplate node/express server with git pre initialized and no view engine specified. <project name> can be replaced with whatever you want to call the app. The files will be created in a directory matching the name you specified in the command. Add that directory to your VS Code workspace.

After the process completes change directory into the root directory of the project with the following command

$ cd <project name>

Initialize a git repository by typing the following command in the terminal:

$ git init

Make sure that you execute this command in the root directory of the project.

In the root directory for the project find the file called app.js and change its name to be server.js. Technically you can leave this file as app.js and everything will work fine but I like to rename this file to server.js to indicate that this is my main configuration file for my backend express server.

Go into the bin directory and open the file called www. Find this line:

var app = require(‘../app’);

Change the path in parenthesis to be:

('../server')

This tells the express server that you changed the main server file from app.js to server.js. Save changes to the www file and close it.

Find the line:

var port = normalizePort(process.env.PORT || '3000');

Change 3000 to 4000

This will prevent a port conflict with the frontend later

Install Node dependencies for the backend

Right click on the root folder for the project and select ‘Open in integrated terminal’

Type the following command:

$ npm install mongoose cors body-parser dotenv

npm stands for node package manager and this command installs publicly available node modules from the npm registry

Mongoose is used to connect with the MongoDB. Dotenv allows the use of .env file for environmental variables.

In the server.js file find this line:

var logger = require('morgan');

Add these lines directly below it:

const bodyParser = require('body-parser');const cors = require('cors');const mongoose = require('mongoose');require('dotenv').config();

These lines tell the express server to import the dependencies you installed above.

In the server.js file find this line:

app.use(express.static(path.join(__dirname, 'public')));

Add these lines directly below it:

app.use(bodyParser.urlencoded({ extended: true }));app.use(cors());

This configures the express server to use the body-parser and cors modules you installed

Run the npm install command again to ensure all node dependencies for the backend are installed properly

$ npm install

Configure the Express server to connect to mongoDB

Add a new file to the root directory of the project call .env (make sure to include the leading period)

In the .env file add the following line:

DB_CONN=<Replace everything after the = with the MongoDB connection URI you got from setting up your account above>

This sets up an environmental variable for the mongodb connection string that will not be uploaded to the code repository since it contains sensitive information. .env files should be included in the .gitignore file so that they do not get pushed to the code respository. Later the environmental variable will be made available to the deployed version of the app through secrets on the kubernetes cluster.

Open the server.js file and find this line:

module.exports = app;

Add these lines directly above it:

mongoose.connect(process.env.DB_CONN, {useNewUrlParser: true,useUnifiedTopology: true}, (err, res) => {if (err) {return console.error(err);}console.log('Connected successfully to mongoDB');});

This code tells the express server to use mongoose to connect to the mongoDB with the uri stored in the DB_CONN environmental variable. It will return a message if the connection is successful or an error if something goes wrong.

Setup Data Schema

In the root of the project folder create a new folder called: models

In the models folder create a new file called: grocery.model.js

Open the grocery.model.js file and add the following code:

const mongoose = require('mongoose');const Grocery = mongoose.model( 'Grocery', new mongoose.Schema({name: String,quantity: Number,bought: Boolean,}));module.exports = Grocery;

This code creates a mongoose data model. This will be the model for the grocery data that is inserted into the mongoDB

Setup API endpoints

Open the folder called routes and add a new file to the folder called: groceries.route.js

Add the following code to the groceries.route.js file:

const express = require('express');const router = express.Router();const Grocery = require('../models/grocery.model')router.get('/all', (req, res) => {Grocery.find((err, foundGroceries) => {if (foundGroceries) {res.send(foundGroceries);} else {res.send(err);}})});router.get('/byID/:id', (req, res) => {Grocery.findById(req.params.id, (err, foundGrocery) => {if (foundGrocery) {res.send(foundGrocery);} else {res.send(err);}})});router.post('/new', (req, res) => {let grocery = new Grocery(req.body);grocery.save((err) =>{if(!err) {res.send('Successfully added a new grocery');} else {res.send(err);}})});router.patch('/update/:id', (req, res) => {Grocery.update({_id: req.params.id}, {$set: req.body}, (err) => {if(!err) {res.send('Successfully updated grocery');} else {res.send(err);}})});router.delete('/delete/:id', (req, res) => {Grocery.deleteOne({_id: req.params.id}, (err) => {if(!err) {res.send('Successfully deleted grocery');} else {res.send(err);}})});module.exports = router;

The first 3 lines import the required dependencies and the data model you created in step 8. Each section starting with router represents an API endpoint. There is an API endpoint for each of the database functions of the app, Create, Retrieve, Update, and Delete. The routes are named to match the action that takes place.

In server.js find this line:

var usersRouter = require('./routes/users');

Add this line directly below it

const groceryRouter = require('./routes/groceries.route');

In server.js find this line:

app.use(‘/users’, usersRouter);

Add this line directly below it:

app.use('/api/groceries', groceryRouter);

Test the backend

Ensure all files are saved

In a terminal pointing to the root directory of the project type the following command:

$ npm start

This will start the node/express server running on localhost. If everything starts correctly you will see a message “Connected successfully to mongoDB”. If there are any issues an error will be output in the terminal. Troubleshoot any errors and test the API endpoints using postman (https://www.postman.com/). The next part in the series will be to build the React frontend.

--

--

No responses yet