Standardized Health Checks for Microservices in TypeScript
Tags: typescript, microservices, javascript, node
Published: 2019-05-21 22:56:55

---
title: Standardized Health Checks for Microservices in TypeScript
description: Assess the health of your microservice by utilizing standardized health checks
published: true
tags: typescript, microservices, javascript, node
canonical_url: https://elliotdenolf.com/posts/standardized-health-checks-in-typescript/
---

> Originally posted on my blog at https://elliotdenolf.com/posts/standardized-health-checks-in-typescript

Assessing the health of your overall system is vital when working with microservices. Just because a service is up and running does not necessarily mean it is able to successfully service a request. Enter **health checks**. Health checks provide a way of evaluating if a service is _not only_ up and running but also fully prepared to service requests. Each service exposes an endpoint that reveals the health of itself as well as any downstream dependencies. Some examples of possible dependencies are other microservices, a database connection, or a service’s own configuration. If a service is deemed to be unhealthy, traffic can be routed elsewhere and the service can be rotated out.

This post will go through how to implement a standardized health check of a downstream dependency in an easily repeatable way for other types of dependencies.

Let’s start off defining an abstract class that must be implemented by all health indicators and a `ResourceHealth` enum to represent each resource’s health.

```typescript
// health-indicator.ts
export abstract class HealthIndicator {
  abstract name: string;
  status: ResourceHealth = ResourceHealth.Unhealthy;
  details: string | undefined;

  abstract checkHealth(): Promise<void>;
}

// resource-health.enum.ts
export enum ResourceHealth {
  Healthy = 'HEALTHY',
  Unhealthy = 'UNHEALTHY'
}
```

Each health indicator:

- Starts out in the `Unhealthy` state by default until it can be verified as `Healthy`
- Must implement the `checkHealth()` function, which has the ability to modify the `status`

The downstream dependency that we will be verifying is a JSON api that exposes a `/ping` endpoint. Here is the implementation:

```typescript
// some-service.check.ts
export class SomeServiceCheck extends HealthIndicator {
  name: string = 'Some Service';

  async checkHealth(): Promise<void> {
    let result: AxiosResponse<any>;
    try {
      const pingURL = `http://localhost:8080/ping`;
      result = await axios(pingURL);

      if (result.status === 200) {
        this.status = ResourceHealth;
      } else {
        this.status = ResourceHealth.Unhealthy;
        this.details = `Received status: ${result.status}`;
      }
    } catch (e) {
      this.status = ResourceHealth.Unhealthy;
      this.details = e.message;
      console.log(`HEALTH: ${this.name} is unhealthy.`, e.message);
    }
  }
}
```

The `checkHealth()` implementation is using the `axios` library to perform a `GET` request against the `/ping` endpoint, then evaluates the status. If it is a 200, the status will be set to `Healthy`. If some other code is returned or an error occurs, the status will be set to `Unhealthy` and details property will be set.

Next, let’s look at implementing a health check service that will be managing all different types of health indicators and executing them.

```typescript
// health.service.ts
export class HealthService {
  private readonly checks: HealthIndicator[];
  public overallHealth: ResourceHealth = ResourceHealth.Healthy;

  constructor(checks: HealthIndicator[]) {
    this.checks = checks;
  }

  async getHealth(): Promise<HealthCheckResult> {
    await Promise.all(
      this.checks.map(check => check.checkHealth())
    );

    const anyUnhealthy = this.checks.some(item =>
      item.status === ResourceHealth.Unhealthy
    );
    this.overallHealth = anyUnhealthy
      ? ResourceHealth.Unhealthy
      : ResourceHealth.Healthy;

    return {
      status: this.overallHealth,
      results: this.checks
    };
  }
}

type HealthCheckResult = {
  status: ResourceHealth,
  results: HealthIndicator[]
};
```

The `HealthService` does the following things:

- Receives all health indicators to be run in its constructor
- Performs all health checks in a `Promise.all()` statement
- Reports the overall health of the system. This is set to `Healthy` if all downstream dependencies are `Healthy`. If _any_ single dependency is `Unhealthy`, the entire health will be set to `Unhealthy`. The overall health and all downstream dependencies are returned in the `HealthCheckResult` response.

The last piece will be calling this service from a `/health` route on our service. For this example, we will be calling the service from an express router which can be mounted via `app.use(healthRoutes)`.

```typescript
// health.routes.ts
const healthRoutes = Router();

healthRoutes.get('/health', async (req, res) => {
  const healthService = new HealthService(
    [
      new SomeServiceCheck(),
      // Add more checks here...
    ]
  );

  const healthResults = await healthService.getHealth();

  res.status(healthResults.status === ResourceHealth.Healthy ? 200 : 503)
    .send({
      status: healthResults.status, dependencies: healthResults.results
    });
});

export { healthRoutes };
```

When this route is hit, the HealthService will be created with any necessary health indicators, then run all of the checks via `getHealth()`. The top level `status` of response will be of type `ResourceHealth`, either `Healthy` or `Unhealthy` with an associated HTTP status code - `200` for healthy or `503` for unhealthy. It will also have a `results` property showing every dependency by name and the resulting health from the check.

Performing a `curl` against this route will return:

```json
{
  "status": "HEALTHY",
  "dependencies": [
    {
      "name": "Some Service",
      "status": "HEALTHY"
    }
  ]
}
```

Further improvements beyond this example:

- Additional health indicators can be added simply by creating a class that implements our `HealthIndicator` abstract class then passed into the `HealthService`.
- If further checks need to be implemented using HTTP GET requests, another base class could be pulled out of the `SomeServiceCheck` in order to be re-used.
- The `HealthService` should be implemented as a singleton if it is to be called from other parts of your code.

Links:

- **[Full source code for this example](https://github.com/denolfe/blog-code-samples/tree/master/posts/standardized-health-checks)**
- [Microservices.io: Health Check API](https://microservices.io/patterns/observability/health-check-api.html)