Deploying a Production NestJS Server on Heroku

Deploying a Production NestJS Server on Heroku

Follow Josh Morony on

I’ve been writing quite a few tutorials on NestJS recently, but an important step in creating a backend for your application is actually hosting it somewhere. During development you can have your NestJS server running over localhost, but when you are ready to launch your application that server will (usually) need to be accessible to everybody (not just your local machine).

There are many different ways you could go about hosting a NestJS server - you could host it anywhere you are able to host a Node application, whether that’s through a service or just on a server of your own. One particular hosting solution I’ve been continuing to use over the years for Node applications is Heroku. They provide a simple build/deploy process which is particularly nice for Node applications as it will just take a look at your package.json file, spin up the server for you and install/run your application.

The basic approach to hosting a Node application on Heroku is:

  1. Have your package.json file define any required dependencies (e.g. the dependencies that will already be there)
  2. Specify what node version you want to use in package.json
  3. Specify a start script in package.json that tells Heroku how to start your application after installing everything
  4. Push your code up to Heroku using git push heroku master

This process is pretty simple, but things get a little more complicated when we have a complex project structure that includes TypeScript and various other things for development. The only thing we want to actually “host” is the built production files (e.g. what you would find in the dist folder) not the entire project. So, we need to differentiate between what we are doing with local development and what we are doing when we deploy the application, but we don’t want to mess around with configuring things every time we want to deploy the NestJS server.

Ideally, we want a set up where we can just “set and forget” - we want to be able to work as per normal when we are developing the application, and we want to be able to do a simple commit and push to Heroku when we are ready to deploy. That is what we are going to focus on in this tutorial.

1. Getting the NestJS Project Ready

The example I used for this tutorial was just a blank NestJS starter application, you could do the same by creating a new project:

nest new heroku-test-project

or you could just make these changes to an existing NestJS project. There are a few things we will need to configure in the project before moving on.

Modify src/main.ts to use a dynamic port:

await app.listen(process.env.PORT || 3000);

When developing with NestJS we will generally use port 3000 but this won’t work with Heroku. Instead, by adding an optional process.env.PORT to the listen method, it will use that port instead if it is defined.

Modify src/main.ts to enable CORS:

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.enableCors();
  await app.listen(process.env.PORT || 3000);
}

This is optional - you do not need to enable CORS, but if you plan on making cross origin requests to this server (e.g. from an Ionic application to a NestJS server) then you will need to make sure it is enabled.

Create a .gitignore file at the root of your project:

# Specifies intentionally untracked files to ignore when using Git
# http://git-scm.com/docs/gitignore

*~
*.sw[mnpcod]
*.log
*.tmp
*.tmp.*
log.txt
*.sublime-project
*.sublime-workspace
.vscode/
npm-debug.log*

.idea/
.sourcemaps/
.sass-cache/
.tmp/
.versions/
coverage/
www/
dist/
node_modules/
tmp/
temp/
$RECYCLE.BIN/

.DS_Store
Thumbs.db
UserInterfaceState.xcuserstate

We don’t want to push any node_modules or other junk up to Heroku, so we will make sure that doesn’t get comitted.

2. Modify the package.json File

We can mostly leave the package.json file as it is, but we do need to make a couple of changes. First, there seems to be a little bug with the start:prod command which we will be using to start the application on Heroku (instead of the default start command for development). Instead of pointing to dist/main.js we need to make sure it points to dist/src/main.js.

Modify the start:prod command in package.json:

"start:prod": "node dist/src/main.js",

Before we can run the start:prod command we will need to run the prestart:prod command which actually performs the build and creates our dist folder with the built files. To do this, we can add a postinstall script which will automatically run after Heroku has finished installed the dependencies for the project.

Add the following to the "scripts" object in package.json:

"postinstall": "npm run prestart:prod",

3. Create Heroku App

Next up, you will need to create your application on Heroku. If you haven’t used Heroku before, you will need to create a new account and you will also need to install the Heroku CLI. Once you have the CLI installed, you will need to run heroku login to log into your account through the CLI.

Once your application is created on Heroku, it is time to prepare your codebase to be pushed up to it. Assuming that you have not already been using Git for the project, you should initialise a new Git repo with:

git init

You will then need to link it to your Heroku application using the command that was provided to you when you created the app in Heroku. It will look like this:

heroku git:remote -a MY-HEROKU-APP

4. Prepare for Heroku Build

There are a couple of final steps we need to take before we push our code up to Heroku. First, we need to make sure that the environment is configured correctly by running these commands:

heroku config:set NPM_CONFIG_PRODUCTION=false
heroku config:set NODE_ENV=production

The NODE_ENV config should already be set to production by default, but we are changing NPM_CONFIG_PRODUCTION to false. Since we are performing the TypeScript build on the server (this is what our postinstall script does) we need all of the devDependencies in package.json for it to run properly. If NPM_CONFIG_PRODUCTION is set to true the devDependencies won’t be installed and it won’t work.

Finally, we just need to create a Procfile. This allows us to specifically supply the command we want to run to start the application rather than the default start script (which we want to keep for our local development environment).

Create a file called Procfile at the root of your project and add the following:

web: npm run start:prod

5. Push Your Code

Now all we need to do is push our code up to Heroku. You can do that by running:

git add .
git commit -m "doing it live"
git push heroku master

Whenever you want to push new code up to Heroku, just add - commit - push heroku master.

Summary

I’m not 100% sure that this process can’t be improved as this isn’t really officially documented - this is just something I’ve played around with that works for me without having to jump through too many hoops. If you have your own process for deploying NestJS applications please feel free to post about it in the comments.

Check out my latest videos: