Build a Simple IoT Device Tracker and Deploy to AWS EC2
By: Li-Ting Liao
Tags: Full Stack, Ec2, Mern

#MERN webstack #AWS EC2

This is a quick demo of how to build a web app full-stack, from front to back, and make it interact with MongoDB Atlas.



http://52.198.4.100/ # Whenever I start running my server on EC2, this link will work, or you will see a blank list :D

Demo

A full-stack demo app with dummy data.

Main page — Logged Devices List was empty.

Add account.

Once added, name will disappear for re-entry.

After multiple accounts created, accounts are shown here on MongoDB Atlas.

When creating new device log, a list of account name will load from database.

Add the first device.

Once the first device was logged, it will show up on main device list.

Repeat all above steps, multiple entries are added.

Try edit — account Other is updated.

The change is shown here in the last row.

Try delete — it’s gone!

The device list is also shown on my MongoDB Atlas.

Main Steps

Install packages and set up initial files

Set up backend .js files

Set up frontend .js files

Deploy to AWS EC2

File Structure

Only show those added manually. We need to $npm build start is because JSX needs to be converted into javascript before deploying. These converted files are stored in the build folder after bundling and minification.

Initiate Project

Code Snippets in Steps

Create server.js in /backend. Create connection to MongoDB:

Create /backend/models, and create device.model.js and account.model.js. In account.model.js, create schema. Account name will be unique, no whitespace, etc.:

In device.model.js, we have 4 fields:

Create /backend/routes, and create devices.js and accounts.js. Go back to server.js first. We’re telling the server to use those files — devices.js and accounts.js.

So when people go to /devices page, the server uses devicesRouter i.e. ./routes/devices.js to render the page.

In accounts.js, handle incoming HTTP get request from ‘/’ route e.g. .find() will give a list of all users in mongoDB. After find the promise, server will return users in json format. If there’s error, there’ll be error message:

In the ‘/add’ route, in request body there’ll be username and then create a new account instance to save into MongoDB. Eventually server will return a response in json:

In devices.js, apply similar logic but add e.g. update by id:

We can now test our routes by Postman. Use Postman “body + raw + json” to test one of the routes e.g. ‘/update/:id’ by Post method:

Added!

Also shown on MongoDB.

So far backend is ready. Now let’s take a look at frontend.

Add below code in /src/index.js. Here App Component is the main component in React which acts as a container for all other components:

Import all separated components files in App.js and start to add routers here. I will add each component accordingly. Here I only show navbar and create-device components details:

In navbar component, Link path is same as what’s stated in App.js. It will help direct users to different components:

In create-device component, add a constructor. The constructor aids in constructing things like defaulting some properties of the created object.

There’s a note on React document specifying that in the constructor, we should add super(props) before any other statement. Simply put, super refers to the parent class constructor. Only after we called the parent constructor’s property (here is the React.Component) then we can use this.state to overwrite that property and set local state of the object.

The first step in create-device component is like:

In this.state, accounts is an “empty” array because we will have a drop-down list for users to choose different accounts.

We can then use componentDidMount() to make sure the component will load from database (axios will help us get an array of accounts which is each document object’s accountname value) after the HTML from render has finished loading, and set the first account name as default display:

Since there’ll be textboxes to input account name, description, duration, etc. The following methods were created. Target is the textbox, and this.setState() will make sure the value inside the textbox will change according to whatever is input by users.

When rendering the page, using React events only requires adding a listener when the element is initially rendered, so in render part, it will look like this.methodName:

Add submit method. e.preventDefault() helps preventing html submit form from taking place i.e. making the submit function clean before we specify what to pass to database. Here when we’re working on connecting frontend to backend, we’re asking frontend to send HTTP request to backend. Here I tried axios library to send HTTP request.

Once submit is clicked, send device info to path ‘/devices’ which is same as server.js, then server.js will then jump to /routes/devices.js, and ‘/add’, submit button will help us send the data to MongoDB to save data there.

In React constructors, other than setting initial local state of the object, we can also bind event handlers methods to the object. Since onSubmit will change variable state by this.setState. Make sure this in methods are bound to this.state’s this:

Deploy

Launch an EC2 instance with Ubuntu 18.04

Protocol is TCP: Nginx port 80, Node port 5000, MongoDB port 27017

Log in EC2

$ ssh -i <our .pem file> <VM>@<Public DNS of our instance>

Create two folders — client (also a deploy folder) & server here

$ cd /home/ubuntu

$ sudo mkdir server

$ sudo mkdir client

$ cd client

$ sudo mkdir deploy # nginx will save all log files in server_logs folder

$ sudo mkdir server_logs

Install packages

$ sudo apt-get install nodejs

$ sudo apt-get install nginx # download node.js dependencies on the server side

$ sudo apt install npm

$ cd /home/ubuntu/server

$ npm install

Edit nginx conf files — customize nginx files as needed

# clear all default settings: Ctrl + k

$ sudo nano /etc/nginx/nginx.conf # another conf file:

$ cd /etc/nginx/conf.d

$ sudo touch default.conf

$ sudo nano default.conf # once complete, restart nginx

$ sudo service nginx restart

Make sure our EC2 instance public ip is whitelisted on MongoDB

Whenever I want to see this web app running, I will run below before I go to my web app public ip http://52.198.4.100/:

$ ssh -i <our .pem file> <VM>@<Public DNS of our instance>

$ cd /home/ubuntu/server

$ npm run start

Features to improve

Below are several features I may be able to extend.

Make it more visual appealing

Add submit success messages if possible

That’s it! This post was inspired by these two materials, here and here.

Thanks for reading and have a wonderful day ahead!