Deploying React Node TypeScript App On Heroku with Github Integration

The following steps were taken to deploy a Node server app and a React client app onto Heroku, both written in TypeScript:

  1. Create new Heroku app heroku-new-app
  2. Link the app with a Github repoheroku-github-integration
  3. Choose whether to have automated or manual deployments. Automated deployments will deploy whenever you push to the Github repo. For manual deployments, you need to tell Heroku when to deploy your app from Github. I went with manual deployments and created a deploy branch in Github to push all my deployment specific updates to that I later merged into my master branch.

heroku-automatic-manual-deployment

Deployment Gotchas

  • In package.json, create a post install script to compile TypeScript files to JavaScript. Also, update the “start” script to run node instead of nodemon:
"scripts":{
   "start":"node src/server.js",
   "postinstall":"tsc"
},
  • Make sure your package.json file specifies ALL packages, including ones that you have installed globally. For example, I had nodemon installed globally so it wasn’t in my package.json so I had to install it locally. I had to do the same thing with the typescript package which was installed globally. I was getting this error on Heroku when deploying my app:
nodemon: not found
  • You can test the deployment by copying your source TypeScript files, package.json, tsconfig.json and any other version controlled files into a separate folder and running:
npm install --production

This will install all node package as specified in package.json and try and run your app.

  • on the server, change the start script to use node instead of nodemon:
    "scripts":{
       "start": "node src/server.js",
    }
  • @types/* npm packages should be installed as regular dependencies in package.json, NOT as dev dependencies. This is because your build (tsc) will need to run on Heroku so the type definition npm packages will be necessary in order to build your TypeScript project.
  • TypeScript should be installed as a local package. For the server app, Heroku will need to build your app with the tsc TypeScript compiler as a post install step. In my case, I had it installed globally, so I just added this line to the list of dependencies in package.json:
"typescript": "^2.9.2"
  • Node version should be specified explicitly in package.json:
"engines":{
   "node":"10.5.0"
},
  • update the server port to be dynamically read from the PORT environment variable. If you just have a hard coded port value to listen on, you will get this error:
Error R10 (Boot timeout) -> Web process failed to bind to $PORT within 60 seconds of launch

To fix the issue, update the server this way:

const app = express();
...
let listenPort = process.env.PORT || 3000;

app.listen(listenPort, ()=> {
  console.log("server running on port "+ listenPort);
})

You will now get a message like this from the Heroku logs after redeploying:

server running on port 4697

Note that this is NOT the port to access your app from a browser or Postman. You should use the normal URL (without specifying a port so it will use the default port 80):

https://vast-everglades-43459.herokuapp.com/

NOT this way:

https://vast-everglades-43459.herokuapp.com:4697/

Runtime configuration variables – Server

Create runtime environment variables on Heroku as required for things like connecting to the database, API keys, etc.

heroku-configuration-variables

The variables can then be accessed in your app like this (assuming there’s an environment variable called FD_CLARIFAI_KEY):

const CLARIFAI_KEY = process.env.FD_CLARIFAI_KEY as string

Setting the environment variables locally on my Windows machine, I just run a batch file which contains this:

SET FD_CLARIFAI_KEY=1234567890abcdefg

Module Import Errors

Getting an error message of Module not found: Can’t resolve… means there is a letter casing issue. This is because Heroku is run on Linux where folder casing matters but if you develop locally on Windows, folder casing isn’t an issue.

Runtime environment variables – React Client

To add environment variables to a React app built with create-react-app, you will need to add a .env file to the root of your project and define your environment variables there. That will be your default (production) environment variables definition. But then you can have a local file called .env.local to override values from the default.

Deploying React client apps built with create-react-app

For React apps built with create-react-app, the following change needs to be made that’s different when deploying a server app:

  • in package.json, remove the postinstall script, so there is no script to run after installation

Useful Heroku Commands

  • heroku logs – View the log as it’s being updated – very useful for debugging your app:
heroku logs --tail -a app-name
In my case, I did:
heroku logs --tail -a vast-everglades-43459
  • heroku config – view configuration variables for an app:
heroku config -a app-name

In my case, I did:

heroku config -a vast-everglades-43459

Further Resources

Deploying a Node and Browser Typescript Project to Heroku

Typescript runs strange on heroku (Stack Overflow)

Specifying a Node.js Version (Heroku)

Setting the port for node.js server on Heroku (Stack Overflow)

Heroku Runtime Principles – Web Servers

Heroku Configuration Variables

Create react app cannot read environment variable after build (Stack Overflow)

Adding Development Environment Variables In .env (create-react-app)

Resolving Heroku Deployment Errors