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:
- Have your
package.json
file define any required dependencies (e.g. the dependencies that will already be there) - Specify what node version you want to use in
package.json
- Specify a
start
script inpackage.json
that tells Heroku how to start your application after installing everything - 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 inpackage.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 inpackage.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.