Web App: Cloud
Deploying to Heroku

Notes

This is a note!

This is a hint!

This is a warning!

Now that we have a functional web app, we can deploy it to the world wide web! This will make the app available to other devices that want to interact wih it - that is, it permits Things to share data on the Internet: Internet of Things! We are using a hosting service called Heroku - they are by no means the only service (there are many), and not the best for all use cases, but they are a very popular choice for hobbyists and tinkerers because they have a solid free plan and excellent documentation, making them easy for developers to use. They also have all manner of integrations that make them a convenient choice for developers with complex needs, and are a legitamately high quality service (that is to say, we aren't using them just because they are free - they're also just plain good).

Making an App

To get started, visit heroku.com and make an account. When you are registered and logged in, you should see a screen like this, except you won't have any apps yet.

Click on New in the upper right corner, and then Create new app from the dropdown, as below. You'll be taken to a short form to create your app (you may have to vary the name of your app to be unique, since we can't have dozens of intro-to-iot.herokuapp.com websites existing at once!). After it is submitted and you receive notice that your app is ready, select it from your app list.

Setting Config Vars

You'll be taken to a dashboard, where you'll want to select Settings from the sub-menu, followed by Show Config Vars. These config vars are the environment variables that we talked about locally, and just as we need a DATABASE environment variable for the app to run locally, we need to provide one for it to use while running on the cloud.

You should already have an mLab database from the MEN Stack tutorial. Log into your dashboard and find the URL that will allow you to access the database remotely, as highlighted below. Paste it into Heroku as a Config Var named DATABASE, making sure to replace the <dbuser> and <dbpassword> placeholders with the User you created.

Make sure to use the username and password from the database itself, not your mLab account!

Connecting to Heroku

Next, click on Deploy in the submenu and scroll down to the Deploy using Heroku Git. This is how we'll transport our app from our computer to Heroku. Follow the instructions linked in the dashboard to install the Heroku CLI, and then go to your terminal and execute

$ heroku login
When you are logged in, execute
$ heroku git:remote -a intro-to-iot-lesson

Replace "intro-to-iot-lesson" with the name you chose for your app!

Our project is already a git repository, so just make sure that everything is committed. We've included a picture of our commit, in case you need a reminder of the commands.

Configuring to Run on Heroku

Heroku can detect what type of project an app is, and has its own "buildpacks" that it uses to deploy an app once you push it to heroku master. However, for the buildpack to work, our project needs to conform to a few restrictions that Heroku expects to see, so it knows how to handle it.

The first of these is to specify the node and npm versions that you are using in package.json. Without these specified, Heroku will install the most recent versions, which could break your app if you are using dependencies that rely on the versions you use locally.

In the pictures below, we found that in our environment we are using version 6.0.0 for npm and 6.10.1 for node, and added an engines section to our package.json to reflect that. Execute

$ npm -v
$ node -v
to find your versions and enter them into package.json.

You will most likely need to delete the semantic dependency from package.json.

Notice an additional change to package.json, where we have added a "start" script. This allows us to start running our app by executing

$ npm start
instead of
$ node app.js
and Heroku looks for a start script while deploying a Node app. Add the start script.

Finally, remember when we made a .gitignore file to keep our .env from being pushed to where the world could see it? We also want to make sure that the node_modules folder (recall that this is where code for your dependencies lives) is not committed to git. This is because Heroku will do a fresh npm install as part of the build process, and having the node_modules folder in the repository you push will conflict. Open .gitignore, and add node_modules to it, like so:

Execute these commands in terminal to remove the node_modules folder, since it has probably already been committed.

$ rm -r node_modules
$ git status
$ git add .
$ git commit -m "Remove node_modules, update .gitignore, and configure for Heroku"

When you execute git status, your should see lots of red. Looking closely, it should mention removing the node_modules followed, editing .gitignore, and editing package.json.

As a sanity check that node_modules has been removed from git's history, you can execute npm install and then git status. You should see that even though the files have been recreated, they do not appear in git status because .gitignore keeps them from being acknowledged.

Deploying

Since we just committed all of the configuration edits, we should be good to go! Execute

$ git push heroku master

This is an excellent opportunity to talk about remotes. When we push to GitHub, the command is git push origin master, because we named the remote "origin" when we linked it to our repository. Now, with Heroku, we have added a second remote, and can choose where to push our updates. Similarly, where the remote lets you choose where you send changes to, branches let you choose what changes to send. If you created a branch named "cool-feature", you could say git push origin cool-feature, and it would share your changes to cool-feature with origin, without affecting master!

Back in your Heroku dashboard, on the Overview tab of the submenu, you should see an activity log with a working build. Within a couple of minutes, it should turn into a green Build Succeeeded, and deploy the app! You can then visit the app by clicking Open app near the upper right of the dashboard.

However, as shown above, our page does not render! Instead, we receive empty json. Recall that our homepage requires accessing the current color and settings from our database, and the database we linked to the app as a config variable is currently empty. When we were working locally, we made seed routes to solve this problem, but we don't want seed routes publically available! Luckily, there is another option...

Seeding the Database

When we visit a webpage, we are actually sending requests from our browser to the server hosting the application, which we talked about briefly while writing our GET routes for pages and POST routes for submitting forms. However, visiting a page is not the only way to submit requests. There are tools, like Postman, which allow you to send requests to a website.

This is a major security breach. Imagine we didn't want just anyone to submit our color and settings forms, which they usually can't do without visiting the page because they are post requests, and entering a url into the address bar submits a get request. The only way to reach our post routes from the webpage is to submit the forms. Yet, Postman and similar tools permit anyone to send all kinds of requests, meaning they can access our /set-color post route without ever visiting our page or submitting the forms. If we really did care about restricting who can access our routes, we would add measures like user authentication, but we are actually going to exploit this breach. We don't care who sets our color or how they do it, and you get to learn some ninja skills.

If this were an app that I had protection on certain routes for, and still had this seeding-the-database problem, I would write a script on my local computer that connects to my cloud database - using the same url we gave to Heroku as the config variable - and seeds everything I need. Run it once, don't make it public, wipe my hands of it.

Start by visiting https://www.getpostman.com/pricing and downloading the app for the free plan on the left. Install it too, of course.

When you open the app, you will see a screen like below. Configure it to match the picture, paying attention to these changes:

  • The request method is POST
  • The request url is https://intro-to-iot-lesson.herokuapp.com/set-color
  • The Body is in form-data mode, with one key-value pair called color, with whatever color you want to start with. Note that 'color' is the name we gave to the input field of the form. We are essentially prepping the request to send the data it would as though from a form.

Now hit the send button, and wait for a response! You should receive the html for the page in the response body.

It may take a minute if the app has been inactive for more than a half hour because the free plan on Heroku puts apps to "sleep" during inactivity.

Open your mLab account too, and enter the intro-to-iot database. You can see that you have a collection named "settings", with one document in it. If you recall the models we made way back when we started working with mongoose, we named a schema "settings", and referred to it in routes like set-color. That schema has now become a collection, with the document that we edit held within.

Hopefully, all these different names for things and how they work together is starting to make sense! It's a lot of terminology and layers, and the only way to truly understand it is to use it until it starts to make sense.

Unfortunately, the homepage still doesn't work because we also use statistics. Unlike set-color, we do have a seedStats route that we used for convenience when it was local, too. Honestly, it's rather bad form and we should get rid of the route, but let's use it for convenience one last time. Change Postman to submit a GET request to the seedStats route, and observe with joy as the homepage now loads!

Now get rid of the seedStats route (and the seed route for data entries too, while we're at it), and push the new build to Heroku.

You'll find the routes in routes/index.js. Make a git commit, and then git push heroku master to rebuild.