If you have been around for a while in the field of software development, especially web development, then you know how tedious and stressful it has historically been to deploy your source code to a webserver. Most of the time, this was accomplished by uploading it using File Transfer Protocol (FTP). But now we have numerous ways of automating the deployment process. In this tutorial, we will learn how to set up continuous deployment of a Nest.js application to Heroku using CircleCI.

Prerequisites

The following are required and crucial for you to get the best out of this tutorial:

  • Node.js installed on your computer
  • Nest CLI installed on your computer
  • A GitHub account
  • A CircleCI account
  • A Heroku account
  • Although not mandatory, you should know a few things about TypeScript.

Scaffolding a new Nest.js application

To create a new Nest.js application, navigate to the root of your development folder from the terminal and run the following command:

nest new nest-heroku-demo

You will be prompted to choose your preferred package manager. Select npm and hit ENTER on your keyboard to proceed. A new Nest.js application will be created within a folder named nest-heroku-demo and all of its dependencies will be installed.

Once the installation process is complete, move into the newly created project folder and run the application with the following commands:

// move into the project
cd nest-heroku-demo

// start the server
npm run start:dev

You can view the welcome page on the default port 3000.

Default Page

Creating the demo application

We will quickly create a basic Nest.js application with a single endpoint that will be used to render a list of products. To keep things simple, we will create a mock list of products and return it as a response. To begin, stop the application from running on the terminal with CTRL + C and open the project in a code editor of your choice. Next, create a folder named mock within the src folder. Within it, create a file named products.mock.ts. Paste the following content into that file (src/mock/products.mock.ts):

export const PRODUCTS = [
  {
    id: 1,
    name: "First product",
    description: "This is the description for the first product",
    price: "200",
  },
  {
    id: 2,
    name: "Second product",
    description: "This is the description for the second product",
    price: "500",
  },
  {
    id: 3,
    name: "Third product",
    description: "This is the description for the third product",
    price: "800",
  },
  {
    id: 4,
    name: "Fourth product",
    description: "This is the description for the fourth product",
    price: "100",
  },
  {
    id: 5,
    name: "Fifth product",
    description: "This is the description for the fifth product",
    price: "250",
  },
];

The list of products exported here will be returned as a response once an HTTP GET request is sent to the /products endpoint. We will discuss more details about this endpoint in a bit.

Setting up a service

We are going to use the default AppService to return the list of products. Replace the content of src/app.service.ts with the following code:

import { Injectable } from "@nestjs/common";
import { PRODUCTS } from "./mock/products.mock";

@Injectable()
export class AppService {
  products = PRODUCTS;

  async getProducts() {
    return await this.products;
  }
}

In the file above, we imported PRODUCTS from the mock file, and returned the list within the getProducts() method.

Creating the products endpoint

Next, we will create the /products endpoint within the default AppController. To do that, navigate to the src/app.controller.ts file and replace its contents with the following:

import { Controller, Get } from "@nestjs/common";
import { AppService } from "./app.service";

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get("/products")
  getProducts() {
    return this.appService.getProducts();
  }
}

The AppService has already been injected into this controller by default. Here, we created a new method named getProducts with a prefix of /products and called the getProducts() method within the AppService.

Running the application

From within the root of the project, start the application again with npm run start:dev and navigate to http://localhost:3000/products. You will see a page displaying the list of our mocked products in JSON.

Product List on Localhost

Note: I have an extension installed to prettify the JSON.

Pushing to GitHub

See this guide to learn how to push a project to GitHub.

Creating a Heroku app

Setting up a Heroku app is required for deployment. This helps Heroku prepare to receive your source code. To begin:

You will be redirected to a page where you will input the basic details for your app. Enter the preferred name.

Create Heroku app

I named the application nest-heroku-demo. Heroku app names need to be unique, so feel free to use any name that you deem fit and click Create app. For CircleCI to uniquely identify our Heroku application and automatically make a deployment to it, we need to create environment variables. This will be the name of the application we just created and the API Key for our Heroku account. To view your Heroku API key, click on your profile picture and select Account settings from the dropdown.

Account Settings

This will take you to a page to manage your account. Ensure that the Account tab is selected and scroll down to the API Key section.

API Key section

Click Reveal to view the API key, then copy it. Keep it safe as we will need it later.

Adding the CircleCI configuration for continuous deployment

In this section, we will create a CircleCI configuration file where we will write the deployment script for our application. To do that, create a folder within the root of the application with the name .circleci. Then create a file within it and call it config.yml. Paste the following code in this new file:

version: 2.1
orbs:
  heroku: circleci/heroku@1.0.1
workflows:
  heroku_deploy:
    jobs:
      - heroku/deploy-via-git

The configuration file above specifies the version of CircleCI configuration for this project. In the orbs key we invoked the latest version of the Heroku orb available at the time of writing. This orb abstracted the complexity involved in setting up the Heroku CLI, as it will be automatically installed and used to deploy the application to Heroku.

Setting up the project on CircleCI

Now that we have created a Heroku app and set up the configuration to facilitate deployment of our Nest.js application to Heroku by CircleCI, we need to configure our project on CircleCI. Log in to your CircleCI account with the linked GitHub account that contains the repository for our Nest.js application. On the project page, find the name of your project and click Set Up Project.

Set up project

You will be prompted with a couple of options regarding the configuration file. Select the use the .circleci/config.yml in my repo option. Enter the name of the branch where your code is housed on GitHub, then click the Set Up Project button.

Select Config File

Your first workflow will start running, but it will fail.

Failed Build

Now if you click on the job heroku/deploy-via-git from the page above, you will see the details of why the deployment was not successful.

Heroku Key Error

Don’t sweat it, we only need to do the following:

  • Add the details of our Heroku application as environment variables
  • Update the port in src/main.ts within our project
  • Create a Procfile

We will do all these in the next section.

Adding environment variables to CircleCI

For CircleCI to have authenticated access to the Heroku application for the deployment process, we need to add two environment variables from our Heroku account to our CircleCI pipeline.

Go to your project’s settings by clicking Project Settings on the Pipelines page (make sure your project is the currently selected project).

Project settings

On the sidebar menu of the settings page, click Environment Variables.

Click on Add Environment Variable. This will show a prompt where you can enter the variable name and value. The variables required are:

  • HEROKU_APP_NAME: The name of the Heroku application created earlier
  • HEROKU_API_KEY: The Heroku API key that was obtained from your account dashboard on Heroku

Environment variable page

We are almost done setting up all the configuration needed to automate the deployment of our application.

Updating the port in the application

In the codebase, we need to update the main.ts with an option to use a fixed port or a dynamically assigned value. This is because Heroku often dynamically assigns a port to every new application, so setting a fixed value (the default for a Nest.js application) will result in an error. Open src/main.ts and update its content as shown below:

import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(process.env.PORT || 3000); // update this line
}
bootstrap();

With this option, the application can either run on the fixed port of 3000 or any dynamic port specified in a .env file.

Creating a Procfile

Finally, you need to create a new file named Procfile within the root of your application and paste the following content into it:

web: npm run start:prod

This file tells Heroku the command that will be used to execute the application on startup.

Save this file and push your changes to the GitHub repository. Now watch your CircleCI dashboard to see the progress of the deployment.

Deployment success status

The status of the CircleCI pipeline for our project indicated that the build was successful and our application has been deployed. You can confirm this by navigating to the link generated for the application by Heroku. The format of the URL is always the same: https://YOUR_HEROKU_APP_NAME.herokuapp.com/. You can also check out the endpoint of my running instance.

Heroku App

Congratulations! You have just deployed a Nest.js application to Heroku.

Conclusion

In this tutorial, we were able to build a simple Nest.js application, set up a Heroku app, and automate the deployment of our Nest.js application to Heroku. The complete source code for this project can be found here on GitHub.


Oluyemi is a tech enthusiast with a background in Telecommunication Engineering. With a keen interest in solving day-to-day problems encountered by users, he ventured into programming and has since directed his problem-solving skills at building software for both web and mobile. A full-stack software engineer with a passion for sharing knowledge, Oluyemi has published a good number of technical articles and blog posts on several blogs around the world. Being tech-savvy, his hobbies include trying out new programming languages and frameworks.


Oluyemi is a tech enthusiast with a background in Telecommunication Engineering. With a keen interest in solving day-to-day problems encountered by users, he ventured into programming and has since directed his problem solving skills at building software for both web and mobile. A full stack software engineer with a passion for sharing knowledge, Oluyemi has published a good number of technical articles and blog posts on several blogs around the world. Being tech savvy, his hobbies include trying out new programming languages and frameworks.

Read more posts by Olususi Oluyemi