Build a Simple IoT Device Tracker and Deploy to AWS EC2

Li-Ting Liao
Dev Diaries
Published in
6 min readOct 5, 2020
Source

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.

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

Demo

A full-stack demo app with dummy data.
The main page — Logged Devices List was empty.
Add account.
Once added, the name will disappear for re-entry.
After multiple accounts are created, accounts are shown here on MongoDB Atlas.
When creating a new device log, a list of account names will load from the database.
Add the first device.
Once the first device was logged, it will show up on the main device list.
Repeat all the 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 a schema. The 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 a request from ‘/’ route e.g. .find() will give a list of all users in MongoDB. After finding the promise, the server will return users in JSON format. If there’s an error, there’ll be an error message:

In the ‘/add’ route, in the request body, there’ll be a username and then create a new account instance to save into MongoDB. Eventually, the 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 the front end.

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 the navbar component, the Link path is the same as what’s stated in App.js. It will help direct users to different components:

In the 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 the local state of the object.

The first step in the create-device component is like:

In this. state, accounts are 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 the database (Axios will help us get an array of accounts which is each document object’s account name value) after the HTML from render has finished loading, and set the first account name as default display:

Since there’ll be text boxes to the 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 the rendering part, it will look like this.methodName:

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

Once submit is clicked, send device info to the path ‘/devices’ which is the 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 the 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 method is bound to this. state’s this:

Deploy

  • Launch an EC2 instance with Ubuntu 18.04
  • The 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 visually 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!

--

--

Li-Ting Liao
Dev Diaries

Software developer by day, amateur writer by night. Passionate about both code and creativity, and always seeking new ways to learn and grow.