Health Checks help ensure that your backend application is ready to process requests. It is important to have health checks in a complex application with large number of dependencies. In this particular post, we will learn how to implement health check in NestJS applications using Terminus package.

1 – What is a Health Check API?

A health check API is a special REST endpoint that can be used to validate the status of the service along with its dependencies. The special health check API endpoint internally triggers an overall health check of the service. This can include checking of database connections, system properties, disk availability and resource availability.

But why add health checks in the first place?

Once you deploy an application, you need to know if the application is running smoothly. However, to make this check, you ideally don’t want any application-specific business calls.

Health checks offer a way to find out if the application is running smoothly, storage is fine and whether your instance is fit to serve traffic.

Basically, the infrastructure hosting your application keeps checking or pinging this endpoint periodically. If the health check API returns an error status code, the infrastructure service can take appropriate action such as trying to restart the application or send a notification.

However, the health check API is simply an implementation. It is equally important to distinguish what makes a service healthy or unhealthy.

Over the years, I’ve seen hundreds of applications getting benefited by even basic health checks. A lot of times, these health checks took care of application restarts and also alerted the team to potential issues.

Having said, let’s look at how we can implement health checks in a NestJS application.

2 – Terminus Health Checks in NestJS

Health checks in NestJS are handled through the Terminus integration.

Terminus is an open-source project by GoDaddy. It provides features such as graceful shutdown and Kubernetes liveness/readiness health checks for HTTP applications. The @nestjs/terminus package brings the capabilities of Terminus to the NestJS ecosystem.

But what is the difference between Liveness and Readiness health checks?

Liveness health check determines whether the container running the application is up. On the other hand, readiness health check is used to determine whether the application is ready to accept incoming requests. Together, both these checks give us a complete picture of our application health.

You can check out godaddy/terminus for more details on Terminus. To use Terminus with NestJS, we need to install the @nestjs/terminus package by using the below command:

npm install --save @nestjs/terminus

3 – How to set up a Health Check in NestJS?

Setting up a health check boils down to creating a health check URL. This URL checks our application’s health and throws an error code if the application is unhealthy. A health check is typically a combination of several health indicators. Basically, a health check is positive if all the health indicators are up and running. In other words, managing the various indicators is key to building an effective health check mechanism.

The @nestjs/terminus package provides a set of predefined indicators. Each predefined indicator focuses on a specific dimension such as an HTTP check, database connectivity check, or disk and memory check.

Depending on the requirements of our application, we can use specific predefined health check indicators.

Let us look at some of the import NestJS health indicators one-by-one.

4 – NestJS Terminus HttpHealthIndicator

The first basic health indicator is the HttpHealthIndicator. To use it, we have to install the @nestjs/axios package.

$ npm install --save @nestjs/axios

Next, we need to create a HealthCheckController as below:

import { Controller, Get } from "@nestjs/common";
import { HealthCheck, HealthCheckService } from "@nestjs/terminus";

@Controller('health')
export class HealthCheckController {
    constructor(
        private healthCheckService: HealthCheckService,
    ){}

    @Get()
    @HealthCheck()
    checkHealth() {
        return this.healthCheckService.check([]);
    }
}

As you can see, this is a typical NestJS Controller with one @Get request handler. In the constructor, we inject an instance of the HealthCheckService from the @nestjs/terminus package. We call the check() method of the HealthCheckService. This method takes an array of objects as input. Basically, we can configure multiple health check indicators using this array. However, in this case, we provide an empty array as input.

Also, we need to import the TerminusModule and HttpModule in our App Module.

import { HttpModule } from '@nestjs/axios';
import { Module } from '@nestjs/common';
import { TerminusModule } from '@nestjs/terminus';

@Module({
  imports: [
    TerminusModule, HttpModule
  ],
  controllers: [AppController, HealthCheckController],
  providers: [AppService],
})
export class AppModule {}

Notice that the above snippet only shows the imports pertaining to the health check functionality. Depending on your application, you may have other imports. You can also create a separate health module and place these imports in that module in case you want to isolate the health check functionality.

When we run the application with the current setup and visit http://localhost:3000/health, we get the below response. Basically, this is a standard response with not much information.

{
	"status": "ok",
	"info": {},
	"error": {},
	"details": {}
}

We can enhance this by modifying the health controller logic as below:

import { Controller, Get } from "@nestjs/common";
import { HealthCheck, HealthCheckService, HttpHealthIndicator } from "@nestjs/terminus";

@Controller('health')
export class HealthCheckController {
    constructor(
        private healthCheckService: HealthCheckService,
        private http: HttpHealthIndicator,
    ){}

    @Get()
    @HealthCheck()
    checkHealth() {
        return this.healthCheckService.check([
            () => this.http.pingCheck('Basic Check', 'http://localhost:3000')
        ]);
    }
}

We use the pingCheck() method from the HttpHealthIndicator package to ping http://localhost:3000. In this case, we get the below response.

{
	"status": "ok",
	"info": {
		"Basic Check": {
			"status": "up"
		}
	},
	"error": {},
	"details": {
		"Basic Check": {
			"status": "up"
		}
	}
}

The response object has quite a bit of information now. Let us understand what information is present in the object.

5 – NestJS HealthCheck Response Object

As seen in the previous section, the /health endpoint returns an object with some important properties:

  • status – if all health indicators are positive, the status is ‘ok’. In case of any failure, the status will have a value ‘error’. When the application is shutting down, the status will change to ‘shutting_down’.
  • info – this object has information about each health indicator that is currently healthy. In other words, the status is ‘up’ if a particular indicator is positive
  • error – this object has information about every indicator that is currently unhealthy. The status is ‘down’ if a particular indicator is negative.
  • details – it contains detail of every health indicator we currently have.

6 – NestJS Terminus DB Health Check

We saw a basic HTTP health check in previous sections.

However, as part of health checks, we often need to verify whether our application database is up.

The @nestjs/terminus package provides special indicators to check database health for various options such as TypeORM, Sequelize and Mongoose.

6.1 – Terminus TypeOrmHealthIndicator

TypeORM is one of most popular ORMs. NestJS TypeORM integration can help our NestJS applications connect to various databases such as MySQL or Postgres.

To implement NestJS database health check via TypeORM, we can use the TypeOrmHealthIndicator from @nestjs/terminus package.

See below example:

import { Controller, Get } from "@nestjs/common";
import { HealthCheck, HealthCheckService, HttpHealthIndicator, TypeOrmHealthIndicator } from "@nestjs/terminus";

@Controller('health')
export class HealthCheckController {
    constructor(
        private healthCheckService: HealthCheckService,
        private http: HttpHealthIndicator,
        private db: TypeOrmHealthIndicator,
    ){}

    @Get()
    @HealthCheck()
    checkHealth() {
        return this.healthCheckService.check([
            () => this.http.pingCheck('Basic Check', 'http://localhost:3000'),
            () => this.db.pingCheck('library')
        ]);
    }
}

We use the pingCheck() method available as part of the TypeOrmHealthIndicator class from the terminus package.

Behind the scenes, the TypeOrmHealthIndicator executes a SELECT 1 SQL query to verify whether the database is alive. In case the database server is up and running, we get the below response.

{
	"status": "ok",
	"info": {
		"Basic Check": {
			"status": "up"
		},
		"library": {
			"status": "up"
		}
	},
	"error": {},
	"details": {
		"Basic Check": {
			"status": "up"
		},
		"library": {
			"status": "up"
		}
	}
}

When the database is down and we call the /health endpoint, we get the below error response.

{
	"status": "error",
	"info": {
		"Basic Check": {
			"status": "up"
		}
	},
	"error": {
		"library": {
			"status": "down"
		}
	},
	"details": {
		"Basic Check": {
			"status": "up"
		},
		"library": {
			"status": "down"
		}
	}
}

The database check is showing status as ‘down’. The overall status of the health check is ‘error’ and the HTTP status code is 503 (Service Unavailable).

6.2 – Terminus SequelizeHealthIndicator

In case we are using NestJS Sequelize as our database integration approach, we can use the Terminus SequelizeHealthIndicator to enable health-checks.

The usage is not too different from TypeORM.

See below example:

import { Controller, Get } from "@nestjs/common";
import { HealthCheck, HealthCheckService, HttpHealthIndicator, SequelizeHealthIndicator } from "@nestjs/terminus";

@Controller('health')
export class HealthCheckController {
    constructor(
        private healthCheckService: HealthCheckService,
        private http: HttpHealthIndicator,
        private db: SequelizeHealthIndicator,
    ){}

    @Get()
    @HealthCheck()
    checkHealth() {
        return this.healthCheckService.check([
            () => this.http.pingCheck('Basic Check', 'http://localhost:3000'),
            () => this.db.pingCheck('sequelize')
        ]);
    }
}

Basically, we use the SequelizeHealthIndicator class available as part of the @nestjs/terminus package.

6.3 – Terminus MongooseHealthIndicator

MongoDB is another popular database option.

To facilitate NestJS health checks for MongoDB, we use the MongooseHealthIndicator class. Basically, the mongoose health indicator helps us use pingCheck() method on a MongoDB database.

See below example:

import { Controller, Get } from "@nestjs/common";
import { HealthCheck, HealthCheckService, HttpHealthIndicator, MongooseHealthIndicator } from "@nestjs/terminus";

@Controller('health')
export class HealthCheckController {
    constructor(
        private healthCheckService: HealthCheckService,
        private http: HttpHealthIndicator,
        private db: MongooseHealthIndicator,
    ){}

    @Get()
    @HealthCheck()
    checkHealth() {
        return this.healthCheckService.check([
            () => this.http.pingCheck('Basic Check', 'http://localhost:3000'),
            () => this.db.pingCheck('mongoose')
        ]);
    }
}

If interested, you can check out our detailed post on building a NestJS MongoDB CRUD application.

7 – NestJS Redis HealthCheck

NestJS Redis Health Check allows us to check whether the Redis instance for our microservice application is up and running.

Currently, there is no special class for enabling Redis health checks in NestJS. However, we can perform NestJS Redis Health Check using the MicroservicesHealthIndicator from @nestjs/terminus package.

See below example:

import { Controller, Get } from "@nestjs/common";
import { RedisOptions } from '@nestjs/microservices';
import { HealthCheck, HealthCheckService, HttpHealthIndicator, MicroserviceHealthIndicator } from "@nestjs/terminus";

@Controller('health')
export class HealthCheckController {
    constructor(
        private healthCheckService: HealthCheckService,
        private http: HttpHealthIndicator,
        private microservice: MicroserviceHealthIndicator
    ){}

    @Get()
    @HealthCheck()
    checkHealth() {
        return this.healthCheckService.check([
            () => this.http.pingCheck('Basic Check', 'http://localhost:3000'),
            () => this.microservice.pingCheck<RedisOptions>('redis', {
                 transport: Transport.REDIS,
                 options: {
                 url: 'redis://localhost:6379',
                },
           }),
        ]);
    }
}

As you can see, we import the MicroserviceHealthIndicator class. Next, we call the pingCheck() method to check whether our Redis instance is up and running. To make this work, we have to specify the transport type and the Redis instance URL.

8 – NestJS Custom Health Check Indicator

We can also implement our own custom NestJS health check indicator if the standard ones are not sufficient.

To do so, we have to first to define the health check logic. This is nothing but a NestJS Service that represents a custom indicator. See below:

import { Injectable } from "@nestjs/common";
import { HealthCheckError, HealthIndicator, HealthIndicatorResult } from "@nestjs/terminus";

@Injectable()
export class MaintenanceHealthIndicator extends HealthIndicator {
    private maintenanceMode: boolean;

    async isMaintenanceModeActivated(key: string): Promise<HealthIndicatorResult> {
        const maintenanceMode = true;

        if (!maintenanceMode) {
            return this.getStatus(key, true);
        }

        throw new HealthCheckError("Maintenance Mode On", this.getStatus(key, false))
    }
}

As you can see, this is a custom class that extends HealthIndicator class from @nestjs/terminus package.

We create a method isMaintenanceModeActivated() that returns a Promise. Based on a boolean variable maintenanceMode, we return the output of this.getStatus() method. This method takes a key and status as input. Key is nothing but an identifier string. The status is boolean with a value of true. Here, true means the health indicator is positive. In case, the maintenanceMode has a value true, our method will throw HealthCheckError with the health status as false. If the value of maintenanceMode is false, the health indicator will be positive Based on our requirement, we can make the custom health method as complex as we want.

Note that this class is decorated with @Injectable().

We can now use this custom health check in our health check controller as below:

import { Controller, Get } from "@nestjs/common";
import { HealthCheck, HealthCheckService, HttpHealthIndicator } from "@nestjs/terminus";
import { MaintenanceHealthIndicator } from "./maintenance.health";

@Controller('health')
export class HealthCheckController {
    constructor(
        private healthCheckService: HealthCheckService,
        private http: HttpHealthIndicator,
        private maintenanceCheck: MaintenanceHealthIndicator
    ){}

    @Get()
    @HealthCheck()
    checkHealth() {
        return this.healthCheckService.check([
            () => this.http.pingCheck('Basic Check', 'http://localhost:3000'),
            () => this.maintenanceCheck.isMaintenanceModeActivated('maintenanceCheck')
        ]);
    }
}

Basically, we import the MaintenanceHealthIndicator class and call the isMaintenanceModeActivated() method.

For the above code, we get the below response.

{
	"status": "error",
	"info": {
		"Basic Check": {
			"status": "up"
		}
	},
	"error": {
		"maintenanceCheck": {
			"status": "down"
		}
	},
	"details": {
		"Basic Check": {
			"status": "up"
		},
		"maintenanceCheck": {
			"status": "down"
		}
	}
}

As expected, the custom health indicator is showing the status as down since maintenanceMode was hard-coded to true. Hence, the health check is having an overall status of error.

Conclusion

With this, we have covered the topic of NestJS Health Check with Terminus package. We went through all the important health check indicators such as HTTP, Database, Redis and Custom along with code examples.

Want to learn about managing environment variables in NestJS? Check out this detailed post on NestJS Config.

If you have any comments or queries about this post, please feel free to mention them in the comments section below.

Categories: BlogNestJS

Saurabh Dashora

Saurabh is a Software Architect with over 12 years of experience. He has worked on large-scale distributed systems across various domains and organizations. He is also a passionate Technical Writer and loves sharing knowledge in the community.

2 Comments

Oyinkansola · June 22, 2022 at 12:16 pm

This was helpful thank you. Can you create another post as a sequel to this one on what to do if you have failing health checks.

    Saurabh Dashora · June 23, 2022 at 1:30 am

    Thanks for the great feedback! Will try for the second part…

Leave a Reply

Your email address will not be published. Required fields are marked *