Before automation was available to software development teams, bottlenecks, repetitive tasks, and human error were constant problems. Active human brains trying to perform mundane repetitive tasks are better suited to problem solving and creative coding. Automation in continuous integration and continuous deployment (CI/CD) has freed up human brainpower for more important tasks. CI/CD has made it more feasible to automatically deploy updates to software applications.

In this tutorial, I will show you how to automate deployment of a LoopBack app to Heroku. Using CircleCI, I will guide you through setting up and implementing a continuous deployment (CD) pipeline. The CD pipeline will enable you to release updates to your application whenever changes are pushed.

This tutorial is the second in a series about establishing a CI/CD practice for your LoopBack applications. The previous tutorial shows you how to automate testing for LoopBack APIs.

Prerequisites

Before you start, make sure these items are installed on your system:

  • A minimum Node.js version of 18
  • An up-to-date JavaScript package manager such as NPM or Yarn
  • The LoopBack 4 CLI

For repository management and continuous integration/continuous deployment, you need:

What are LoopBack and Heroku?

LoopBack is a highly extensible Node.js and TypeScript framework for building APIs and microservices. Heroku is a platform as a service (PaaS) that enables developers to build, run, and operate applications entirely in the cloud. By taking care of the more demanding aspects of maintaining a cloud-based application, Heroku allows developers to focus on building apps instead of managing servers.

This project began in a previous tutorial where we built an API for a quiz application.

To recap: the API has endpoints to handle the following operations:

  1. Get all questions
  2. Get the total number of questions in the database
  3. Get a question with a specific ID

Getting started

If you have not yet done so, install the LoopBack 4 CLI by running this command:

npm install -g @loopback/cli

Scaffold a new project using the Loopback CLI command:

lb4 app

When prompted by the CLI, respond as shown below. Note that a different application class name from the default is specified:

? Project name: loopback-api-heroku
? Project description: Loopback project to demonstrate automated deployment to heroku
? Project root directory: loopback-api-heroku
? Application class name: LoopbackApiHerokuApplication
? Select features to enable in the project Enable eslint, Enable prettier, Enable mocha, Enable loopbackBuild, Enable vscode, Enable docker, Enable repositories, Enable
services
? Yarn is available. Do you prefer to use it by default? No

Configuring Heroku

The next thing to do is create a new application on Heroku. From the Heroku dashboard, click New, then New App. Fill in the form. You can use a different name and region for the app if you prefer.

Create Heroku App

Next, click the Create app button to complete the creation process. You will then be redirected to the Deploy view for your newly created application.

Next up is adding a buildpack; just click the Settings tab. In the buildpacks section, click Add buildpack.

Add buildpack

A form allows you to select an officially supported buildpack or provide a URL for one. Select nodejs to use the officially supported Heroku nodejs buildpack. Click Save changes. NodeJS will be used to build your next deployment.

The last thing you need is an API key. You will use this along with the app name to connect the CircleCI pipeline to Heroku. To get your API key, open the Account Settings page and scroll down to the API key section.

![Reveal API Key]2023-10-14-reveal-api-key

Click the Reveal button and copy the API key that is displayed.

Next, at the root of your project, create a new file called Procfile. Note that this file has no extension. In it, add:

web: node .

Without this procfile, Heroku will default to starting the app by calling npm run. npm run tries to run the build process, which will lead to an application error because the dev dependencies for the application have not been installed.

Configuring CircleCI

Next, add the pipeline configuration for CircleCI. For this project, the pipeline consists of two steps:

  1. The build step builds the project and installs the project dependencies. Ideally, you should run project tests at this stage. For the sake of brevity, we will skip testing in this demo. Instead, use the pre-built tests that were provided when you created the application.
  2. In the Deploy to Heroku step, when the build stage is completed successfully, you can deploy the latest changes to Heroku.

Creating the CircleCI config file

At the root of your project, create a folder named .circleci and in it, create a file named config.yml. In the newly created file, add this configuration:

version: "2.1"

orbs:
  node: circleci/node@5.1.0

jobs:
  build-and-test:
    docker:
      - image: "cimg/base:stable"
    steps:
      - checkout
      - node/install
      - run:
          command: npm install
          name: Install dependencies
      - run:
          command: npm run test
          name: Run tests
  deploy:
    docker:
      - image: "cimg/base:stable"
    steps:
      - checkout
      - run:
          name: Deploy app to Heroku
          command: |
            git push https://heroku:$HEROKU_API_KEY@git.heroku.com/$HEROKU_APP_NAME.git main

workflows:
  main:
    jobs:
      - build-and-test
      - deploy:
          requires:
            - build-and-test

The config also uses the circleci/node orb. Among other things, this orb allows you to install packages with caching enabled by default.

The config specifies the build-and-test job, which:

  1. Checks out the latest code
  2. Installs node
  3. Installs the packages declared in package.json
  4. Runs the tests in the projects

The second job uses git to deploy your latest code changes to your Heroku project using your account credentials.

The config also specifies a workflow that runs the build-and-test job followed by the deploy job. Notice that there is a requires option that tells CircleCI to run the deploy job only if the build-and-test job has been completed successfully.

Next, set up a repository on GitHub and link the project to CircleCI. See this post for help pushing your project to GitHub.

Before you push your code, take a moment to do some housekeeping. Lint your code and fix any issues using this command:

npm run lint:fix

Log into your CircleCI account. If you signed up with your GitHub account, all your repositories will be displayed on your project’s dashboard.

Next to your loopback-api-heroku project click Set Up Project.

CircleCI detects the config.yml file within the project. Click Use Existing Config and then Start Building. Your first workflow will start running, but it will fail. That is to be expected.

2023-10-14-failed-build

The deployment process fails because you have not provided your Heroku API key. You can fix that now. Click the Project Settings button, then click Environment Variables. Add two new variables:

  • The value for the HEROKU_APP_NAME variable is the app name in Heroku: loopback-api-heroku.
  • HEROKU_API_KEY is Heroku API key that you retrieved from the account settings page.

Select the Rerun Workflow from Failed option to rerun the Heroku deployment. To confirm that your workflow was successful, you can open your newly deployed app in your browser. The URL for your application is: https://<HEROKU_APP_NAME>.herokuapp.com/.

You will be welcomed with your current index page.

Default homepage

Now that there is a pipeline in place, add the questions features of the API.

Building the question model

For this tutorial, a question will have fields for these attributes:

  • Difficulty
  • Question
  • Correct answer

A unique primary key is assigned by default when the question is created.

You can use the lb4 model command and answer the prompts to generate the model. Press Enter with an empty property name to generate the model. Follow these steps:

lb4 model question
? Please select the model base class Entity (A persisted model with an ID)
? Allow additional (free-form) properties? No
Model Question will be created in src/models/question.model.ts

Let's add a property to Question
Enter an empty property name when done

? Enter the property name: id
? Property type: number
? Is id the ID property? Yes
? Is id generated automatically? Yes

Let's add another property to Question
Enter an empty property name when done

? Enter the property name: difficulty
? Property type: string
? Is it required?: Yes

Let's add another property to Question
Enter an empty property name when done

? Enter the property name: question
? Property type: string
? Is it required?: Yes

Let's add another property to Question
Enter an empty property name when done

? Enter the property name: answer
? Property type: string
? Is it required?: Yes

Let's add another property to Question
Enter an empty property name when done

? Enter the property name:

A new model will be created at src/models/question.model.ts.

Building a datasource

Next, create a datasource to hold the questions for the API. For this tutorial, use an in-memory database. Create a datasource using this command:

lb4 datasource

Respond to the prompts as shown:

? Datasource name: db
? Select the connector for db:  In-memory db (supported by StrongLoop)
? window.localStorage key to use for persistence (browser only):
? Full path to file for persistence (server only): ./data/db.json

Next, create a folder named data in the root directory of the project. In the data directory, create a file named db.json and add this to it:

{
  "ids": {
    "Question": 9
  },
  "models": {
    "Question": {
      "1": "{\"difficulty\":\"medium\",\"question\":\"The HTML5 standard was published in 2014.\",\"answer\":\"True\",\"id\":1}",
      "2": "{\"difficulty\":\"medium\",\"question\":\"Which computer hardware device provides an interface for all other connected devices to communicate?\",\"answer\":\"Motherboard\",\"id\":2}",
      "3": "{\"difficulty\":\"medium\",\"question\":\"On which day did the World Wide Web go online?\",\"answer\":\"December 20, 1990\",\"id\":3}",
      "4": "{\"difficulty\":\"medium\",\"question\":\"Android versions are named in alphabetical order.\",\"answer\":\"True\",\"id\":4}",
      "5": "{\"difficulty\":\"medium\",\"question\":\"What was the first Android version specifically optimized for tablets?\",\"answer\":\"Honeycomb\",\"id\":5}",
      "6": "{\"difficulty\":\"medium\",\"question\":\"Which programming language shares its name with an island in Indonesia?\",\"answer\":\"Java\",\"id\":6}",
      "7": "{\"difficulty\":\"medium\",\"question\":\"What does RAID stand for?\",\"answer\":\"Redundant Array of Independent Disks\",\"id\":7}",
      "8": "{\"difficulty\":\"medium\",\"question\":\"Which of the following computer components can be built using only NAND gates?\",\"answer\":\"ALU\",\"id\":8}"
    }
  }
}

The ids key of the JSON file lets the database know the next ID to assign a new question. In the models section, we provide the data for each model. Also specified is the Question model and the base questions in your database.

Create a repository

For this tutorial, you will use the repository to provide a layer of abstraction between the database and the question model. Create a new repository using this command:

lb4 repository

Respond to the prompts as shown:

? Select the datasource DbDatasource
? Select the model(s) you want to generate a repository for Question

The newly created class (located in src/repositories/question.repository.ts) has the connections needed to perform CRUD operations for your model.

Create a controller

Create a new controller using this command:

lb4 controller

Respond to the CLI prompts as shown:

? Controller class name: question
Controller Question will be created in src/controllers/question.controller.ts

? What kind of controller would you like to generate? REST Controller with CRUD functions
? What is the name of the model to use with this CRUD repository? Question
? What is the name of your CRUD repository? QuestionRepository
? What is the name of ID property? id
? What is the type of your ID? number
? Is the id omitted when creating a new instance? Yes
? What is the base HTTP path name of the CRUD operations? /questions

The CLI creates a controller capable of handling all CRUD operations and you don’t need to do anything else.

Commit your code and push the latest changes to your GitHub repository. This triggers the CircleCI build-and-test which runs successfully and deploys your new changes to Heroku - just as it did earlier. Nice work!

Go to https://<HEROKU_APP_NAME>.herokuapp.com/questions. There is the API response containing questions!

API response

Conclusion

In this tutorial, I have shown you how to set up a CI/CD pipeline for a LoopBackJS API using GitHub, CircleCI, and Heroku. By automating the process of releasing new features, the risk of human error impacting the production environment is greatly reduced.

Your valuable developer hours are better applied to the more creative aspects of development. Repetitive, mundane tasks are automated so your team can focus on problem-solving.

The entire codebase for this tutorial is available 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.

Read more posts by Olususi Oluyemi