typescript 61 lines · 3 tabs

NestJS Health Checks With Terminus and a Custom Prisma Indicator

Shared by codesnips Aug 2026
3 tabs
import { Injectable } from '@nestjs/common';
import { HealthIndicator, HealthIndicatorResult, HealthCheckError } from '@nestjs/terminus';
import { PrismaService } from '../prisma/prisma.service';

@Injectable()
export class PrismaHealthIndicator extends HealthIndicator {
  constructor(private readonly prisma: PrismaService) {
    super();
  }

  async isHealthy(key: string): Promise<HealthIndicatorResult> {
    try {
      await this.prisma.$queryRaw`SELECT 1`;
      return this.getStatus(key, true);
    } catch (error) {
      const message = error instanceof Error ? error.message : 'unreachable';
      const result = this.getStatus(key, false, { message });
      throw new HealthCheckError('Prisma check failed', result);
    }
  }
}
3 files · typescript Explain with highlit

This snippet wires up a production-ready health endpoint in NestJS using @nestjs/terminus, the framework's official wrapper around the Terminus library. The goal is to expose the twin probes that container orchestrators like Kubernetes expect: a cheap liveness signal that says the process is up, and a deeper readiness signal that confirms downstream dependencies are actually reachable before traffic is routed to the pod.

The PrismaHealthIndicator in the first tab shows how to build a custom indicator when the built-in TypeOrmHealthIndicator or HTTP checks do not fit. It extends HealthIndicator, which supplies the getStatus helper that formats results into Terminus's expected { key: { status } } shape. The isHealthy method runs a trivial SELECT 1 through Prisma's $queryRaw — the lightest query that still proves the connection pool can round-trip to the database. On success it returns an up result; on failure it wraps the error detail and throws a HealthCheckError, which is the contract Terminus relies on to mark the aggregate check as failed.

Using $queryRaw rather than a full model query is deliberate: it avoids table locks, permission surprises, and schema coupling, so the probe measures connectivity rather than application correctness. Because indicators are ordinary providers, the indicator receives PrismaService through constructor injection like any other service.

The HealthController in the second tab composes the checks. The /health/live route intentionally does nothing but return, so liveness never fails just because the database is slow — that separation prevents a temporary dependency outage from triggering pod restarts. The /health/ready route passes an array of async callbacks to this.health.check, and Terminus runs them, aggregates statuses, and returns HTTP 200 or 503 accordingly. Here it combines the custom prisma.isHealthy check with a disk.checkStorage threshold, demonstrating how multiple indicators cooperate.

The HealthModule tab registers everything by importing TerminusModule and declaring the controller plus the two providers. A common pitfall is forgetting to provide PrismaService in this module's scope, which yields an unresolved-dependency error at boot. Keeping liveness and readiness distinct, and keeping the readiness query minimal, is what makes this pattern safe to hammer at high frequency.


Related snips

Share this code

Here's the card — post it anywhere.

NestJS Health Checks With Terminus and a Custom Prisma Indicator — share card
Link copied