Deploying an ExpressJS (Node) web application to Heroku
By: Uday Hiwarale
Tags: DevOps, Web Development, Cloud, Heroku, Nodejs

Creating a Heroku Application

First of all, we need a Heroku account. To create a free account, use the signup.heroku.com URL and fill in essential details. Make sure to use a valid email address which would be your username. If you already have an account, then you can log in to the dashboard using id.heroku.com/login URL.

Once your login, you will be redirected to the dashboard page. This is where you will see your Heroku apps. You can click on Create new app button to create a new Heroku application and connect to a GitHub repository but we are going to follow another approach of deploying Heroku apps.

Application Structure

We are going to build a simple image resizing application using Node. For the image resizing, we can use the sharp package. We are going to create an Express server that accepts a request on /resize endpoint and returns a resized test PNG image. The default / endpoint returns index.html that contains some information about the /resize endpoint usage.

Let’s create a project directory with the name heroku-image-resize and run $ npm ini -y command to initialize package.json file. Then we need to install the sharp package for resizing the image and the express package.

$ npm install --save sharp express

Once the above dependencies are installed, we can work on the Express HTTP server. Let’s create the server.js file in our project and write a logic to handle / and /resize endpoints. For the test image, I am using Lenna. Let’s download this image and save it as lenna.png in our project.

heroku-image-resize/

├── .gitignore

├── LICENSE

├── README.md

├── lenna.png

├── package-lock.json

├── package.json

├── Procfile

├── server.js

└── www

├── index.html

├── main.js

└── style.css

💡 You can find above files in this GitHub repository.

I will talk about Procfile file later since it is used by Heroku. The www directory contains assets for our web application though you can ignore what’s inside main.js and style.css for now. The index.html renders a sample image from /resize endpoint inside <img> element.

In the above server.js file, we are sending the index.html file on the / endpoint and /www endpoint sends assets from the www/ directory such as style.css and main.js . The /resize endpoint receives :size param which provides dimensions for the image to resize.

The HTTP server is started on the port 80 unless a port is provided through PORT environment variable. This is necessary because Heroku provides a port for the application on which it will receive the web traffic through the PORT environment variable. Let’s start this server by running $ node server.js command which will serve the application on the port 80 .

Once the server is up and running, you can visit http://localhost URL in the browser. This will fetch and render the index.html which displays a resized image and an input to provide the image size. When we click the SUBMIT button, a function inside main.js replaces the src property of the <img> element with /resize/200x200 value (URL).

Create and Deploy Heroku Application

First of all, we need to install the Heroku CLI. To install this CLI, you can follow these instructions. If you are using macOS, then you can install it using the $ brew install heroku/brew/heroku command.

$ heroku --version

Once the CLI is installed, you can use the $ heroku --version command to check the Heroku CLI version. You also need to install Git since Heroku CLI uses Git to push the application code to the Heroku server. If your application directory is not a Git repository, run $ git init command to convert your project into a Git repository.

First of all, we need to log into Heroku from the terminal. Since we are creating and pushing a Heroku application, Heroku needs to know who we are. So use the $ heroku login command and provide your credentials.

$ heroku login

This command will open up a browser window where you would need to provide your Heroku account credentials. Once you are logged in, the terminal will exit and print the email address of your account.

To create a Heroku application, we need to run $ heroku create command from the project directory. But this command creates a project with the name chosen by Heroku which is not ideal. To provide a custom application name, use the following command.

$ heroku apps:create

The $ heroku apps:create --region eu thatisuday-image-resize command creates a Heroku application with the name thatisuday-image-resize . It also generates a Git remote (where it would be pushed) and a subdomain.

💡 The --region flag sets the region for the application deployment. You should choose a region closer to you. For find regions of Heroku, use $ heroku regions command.

(Heroku Dashboard)

Now if you visit the Heroku Dashboard, you should be able to see your application. However, if you visit the https://thatisuday-image-resize.herokuapp.com URL, you won’t be able to see our application since we haven’t pushed the application code to https://git.heroku.com/thatisuday-image-resize.git repository.

$ git remote -v

When we create a Heroku application, Heroku adds heroku remote to the Git repository which points to https://git.heroku.com/<app-name>.git . Since I am working on a GitHub repository, I also have a origin remote.

If you don’t see a heroku remote or you want to add the Heroku application remote to your local repository, you can use $ git remote add heroku <app-remote-url> command. But I would recommend using the $ heroku git:remote -a <app-name> command which resolves remote URL from the app name. You can get the app-name from the Heroku dashboard.

When we deploy the application, Heroku needs to know which command to run in the web dyno. We specify the configuration of the web dyno (and other dynos) in the Procfile. The Procfile is a text file containing the information of the processes (dynos). In its simple form, it looks like below.

<process-name>: <command>

For our application, we want to run node server.js command inside web process (dyno) to start a web server. Therefore our Procfile would look like below. If you need more info on Procfile , follow this documentation.

web: node server.js

When we deploy the application, based on package.json file in the application repository and other buildpack detection methods employed by Heroku, Heroku runs the node server.js command in the web dyno using the latest LTS installation of Node.js available.

However, I would recommend setting a custom version of Node.js to use to run the application. This way, if our application has incompatibility issues with a certain version of Node.js, we can detect it on our local machine. To provide a Node.js version, use package.json file’s engines property.

{

"name": "myapp",

"engines": {

"node": "12.16.2"

}

}

I am using the Node.js version 12.16.2 for this application since it’s the version I have installed on my local machine. You can also use 12.x value in which case Heroku will choose the latest minor version of the Node.js. You can also specify NPM or Yarn version as described here.

Before we deploy the application, we want to know if our local Heroku setup is fine. Using the $ heroku local <process> command, we can start a process locally from the Procfile . Let’s start the web process by running $ heroku local web command.

$ heroku local web

This time, instead of running $ node server.js command to start the web-server, we asked Heroku to run the command of web process from the Procfile . Now if you open the http://localhost:5000 URL in the browser, you should be able to see the application running there. The port 5000 is chosen by Heroku and it will provide this port through the PORT environment variable which we are using inside server.js is available.

Now that our application is working fine and we are done with the Heroku setup, it’s time to publish the application. Just so you know, we have already published the application. What we need to do is push application code to the Git repository of the application. So let’s create a commit and push those commits to the heroku remote.

💡 Make sure you have node_modules added to the .gitignore . You should avoid pushing any files to the application repository that’s not needed by the application. The heroku/nodejs buildpack installs the NPM module by running $ npm install -y command automatically.

We should use the main or master branch of the heroku remote to push the application code (commits). When Heroku receives these commits (after running $ git push heroku main command), it identifies the application type (buildpack). You can see the Node.js app detected line in the above log.

Heroku then install the Node.js and NPM (or Yarn) (of the versions defined inside package.json ) and installs all the dependencies mentioned inside package.json . Once these dependencies are installed, it compresses everything into a slug and deploys it to a web dyno.

💡 Sometimes, Heroku can’t detect the buildpack. While deploying the application, we can get failed to detect set buildpack heroku/nodejs error in the terminal. To solve this issue, you should set buildpack manually using $ heroku buildpacks:set heroku/nodejs command as described here.

Now we can access the https://thatisuday-image-resize.herokuapp.com URL in the browser. Our application is now running on the Heroku cloud. To check application history, activity, or to delete the application, use the Heroku dashboard. However, you can check the status of the Heroku application using the $ heroku ps command in the terminal.

$ heroku ps

This command shows the free quota remaining as well as the status of our Heroku application. As you can see, the web dyno (process) is up and running. A free dyno will go to sleep if it receives no traffic for 30 mins to preserve resources but it will wake up as soon as it receives a request.

For the free tier account, you can’t scale the application. This means your application will only work on 1 dyno (which is not bad). However, you can scale down the application. For example, you can set the dyno numbers to 0 using $ heroku ps:scale <process>=<size> command.

$ heroku ps:scale web=0

Now if you visit https://thatisuday-image-resize.herokuapp.com URL again, it will display an error message since our application is no longer running. To scale up dynos again, we need to use the same command with dyno size 1 .

Updating Application (Redeployments)

When we want to modify our application and deploy the app again, we just need to create a Git commit and push it to the main branch of the heroku remote. To know more about deployments using Git, read this documentation.