@eqxjs/nest-healthchecks
v1.2.0
Published
A NestJS library for health checks in the @eqxjs ecosystem.
Keywords
Readme
@eqxjs/nest-healthchecks
A NestJS library providing standardized liveness and readiness health check endpoints for the @eqxjs ecosystem. Built on top of @nestjs/terminus with pluggable health indicators for APIs, databases, caches, and message queues.
Status: development
Node: >=24
Installation
yarn add @eqxjs/nest-healthchecksFeatures
- Liveness & Readiness Endpoints — Kubernetes-compatible health check endpoints
- Built-in Indicators — Pre-configured health checks for common services
- Standardized Response Format — Consistent envelope with numeric result codes
- Flexible Configuration — Sync (
forRoot) and async (forRootAsync) setup - Custom Controllers — Override default controller behavior via class extension
- Based on
@nestjs/terminus— Leverages the official NestJS health check framework
Quick Start
Basic Setup (forRoot)
import { Module } from '@nestjs/common';
import { HealthCheckModule } from '@eqxjs/nest-healthchecks';
@Module({
imports: [
HealthCheckModule.forRoot({
routePrefix: 'health', // default
livenessRoute: 'liveness', // default
readinessRoute: 'readiness', // default
indicators: {
liveness: [],
readiness: [],
},
}),
],
})
export class AppModule {}Async Configuration (forRootAsync)
The useFactory function returns only the indicators config (HealthIndicatorsConfig). Routing options (routePrefix, livenessRoute, readinessRoute) are specified at the top level alongside useFactory.
import { Module } from '@nestjs/common';
import { HealthCheckModule } from '@eqxjs/nest-healthchecks';
import { ConfigModule, ConfigService } from '@nestjs/config';
@Module({
imports: [
HealthCheckModule.forRootAsync({
imports: [ConfigModule],
routePrefix: 'health',
livenessRoute: 'liveness',
readinessRoute: 'readiness',
useFactory: (configService: ConfigService) => ({
liveness: [],
readiness: [],
}),
inject: [ConfigService],
}),
],
})
export class AppModule {}Endpoints
Once configured, two endpoints are available:
| Endpoint | Default path | Description |
| --------- | ----------------------- | -------------------------------------------------------------------------- |
| Liveness | GET /health/liveness | Runs liveness indicators; used by Kubernetes to detect if the pod is alive |
| Readiness | GET /health/readiness | Runs readiness indicators; returns 503 if any check fails |
Response Format
Success (200):
{
"resultCode": 20000,
"resultDescription": "Success",
"resultData": "Healthy",
"resultDetail": {
"mongodb": { "status": "up" },
"http-endpoints": { "status": "up" }
}
}The liveness response omits
resultDetail.
Failure (503):
{
"resultCode": 50300,
"resultDescription": "Service Unavailable",
"resultData": "Unhealthy",
"resultDetail": {
"mongodb": { "status": "down", "message": "Connection timeout" }
}
}Configuration Options
HealthCheckOptions (used with forRoot)
| Option | Type | Default | Description |
| ---------------------- | ----------------------------- | ----------------------- | ------------------------------------- |
| routePrefix | string | 'health' | Base path for both endpoints |
| livenessRoute | string | 'liveness' | Liveness endpoint path segment |
| readinessRoute | string | 'readiness' | Readiness endpoint path segment |
| indicators | HealthIndicatorsConfig | {} | Health indicator functions |
| indicators.liveness | HealthIndicatorFunction[] | [] | Functions run on liveness checks |
| indicators.readiness | HealthIndicatorFunction[] | [] | Functions run on readiness checks |
| controller | Type<HealthCheckController> | HealthCheckController | Custom controller class |
| providers | Provider[] | [] | Extra NestJS providers for the module |
| imports | ModuleMetadata['imports'] | [] | Extra modules to import |
| exports | ModuleMetadata['exports'] | [] | Extra providers to export |
HealthCheckAsyncOptions (used with forRootAsync)
Same routing and module options as above, plus:
| Option | Type | Description |
| ------------ | ------------------------------------------------------------------------ | ----------------------------------- |
| useFactory | (...args) => HealthIndicatorsConfig \| Promise<HealthIndicatorsConfig> | Factory returning indicators config |
| inject | InjectionToken[] | Tokens injected into useFactory |
Built-in Health Indicators
All indicators are NestJS @Injectable() services that require HealthIndicatorService from @nestjs/terminus via constructor injection. They must be registered as providers in forRootAsync and injected into the useFactory function — they cannot be instantiated with new.
Peer Dependencies
Install only the peers for the indicators you use:
| Indicator | Peer dependency |
| --------------------------------- | --------------------------------------- |
| ApiHealthIndicator | (none — uses native fetch) |
| MongodbHealthIndicator | mongodb |
| MssqlHealthIndicator | mssql |
| PostgresHealthIndicator | pg |
| IORedisHealthIndicator | ioredis |
| KafkaJsHealthIndicator | @eqxjs/custom-kafka-server |
| ConfluentKafkaJSHealthIndicator | @eqxjs/kafka-server-confluent-kafkajs |
| ConfluentKafkaHealthIndicator | @eqxjs/kafka-server-confluent-kafka |
API Health Indicator
Checks HTTP/API endpoints via GET requests. Each request has a 5-second timeout; only HTTP 200 is considered healthy.
import { Module } from '@nestjs/common';
import {
HealthCheckModule,
ApiHealthIndicator,
} from '@eqxjs/nest-healthchecks';
@Module({
imports: [
HealthCheckModule.forRootAsync({
providers: [ApiHealthIndicator],
inject: [ApiHealthIndicator],
useFactory: (apiIndicator: ApiHealthIndicator) => ({
readiness: [
() =>
apiIndicator.isHealthy([
{
name: 'User Service',
url: 'https://api.example.com/users/health',
},
{
name: 'Payment Service',
url: 'https://api.example.com/payments/health',
},
]),
],
}),
}),
],
})
export class AppModule {}Signature: isHealthy(endpoints: Array<{ name: string; url: string; method?: HttpMethod; config?: RequestInit & { timeout?: number } }>): Promise<HealthIndicatorResult>
HttpMethod is an enum exported from @eqxjs/nest-healthchecks: GET (default), POST, PUT, DELETE, HEAD, OPTIONS.
The config.timeout field (default 5000 ms) sets the per-request abort timeout. Any additional fields in config are forwarded directly to the native fetch call.
MongoDB Health Indicator
Connects to MongoDB and runs a ping command.
import { Module } from '@nestjs/common';
import {
HealthCheckModule,
MongodbHealthIndicator,
} from '@eqxjs/nest-healthchecks';
@Module({
imports: [
HealthCheckModule.forRootAsync({
providers: [MongodbHealthIndicator],
inject: [MongodbHealthIndicator],
useFactory: (mongoIndicator: MongodbHealthIndicator) => ({
readiness: [
() =>
mongoIndicator.isHealthy(
[
{
dbName: 'users-db',
connectionString: 'mongodb://localhost:27017/users',
},
{
dbName: 'orders-db',
connectionString: 'mongodb://localhost:27017/orders',
},
],
{ timeoutMs: 3000 },
),
],
}),
}),
],
})
export class AppModule {}Signature: isHealthy(connections: Array<{ dbName: string; connectionString: string }>, options?: { database?: string; timeoutMs?: number; clientOptions?: MongoOptions }): Promise<HealthIndicatorResult>
| Option | Default | Description |
| --------------- | --------- | ----------------------------------- |
| timeoutMs | 1500 | Connection and command timeout (ms) |
| database | 'admin' | Database to ping |
| clientOptions | {} | Additional MongoClient options |
PostgreSQL Health Indicator
Connects to PostgreSQL and executes SELECT 1.
import { Module } from '@nestjs/common';
import {
HealthCheckModule,
PostgresHealthIndicator,
} from '@eqxjs/nest-healthchecks';
@Module({
imports: [
HealthCheckModule.forRootAsync({
providers: [PostgresHealthIndicator],
inject: [PostgresHealthIndicator],
useFactory: (postgresIndicator: PostgresHealthIndicator) => ({
readiness: [
() =>
postgresIndicator.isHealthy(
[
{
dbName: 'main-db',
connectionString: 'postgres://user:pass@localhost:5432/main',
},
],
{ timeoutMs: 2000 },
),
],
}),
}),
],
})
export class AppModule {}Signature: isHealthy(connections: Array<{ dbName: string; connectionString: string }>, options: { timeoutMs?: number }): Promise<HealthIndicatorResult>
| Option | Default | Description |
| ----------- | ------- | ------------------ |
| timeoutMs | 1500 | Query timeout (ms) |
MSSQL Health Indicator
Connects to Microsoft SQL Server and executes SELECT 1.
import { Module } from '@nestjs/common';
import {
HealthCheckModule,
MssqlHealthIndicator,
} from '@eqxjs/nest-healthchecks';
@Module({
imports: [
HealthCheckModule.forRootAsync({
providers: [MssqlHealthIndicator],
inject: [MssqlHealthIndicator],
useFactory: (mssqlIndicator: MssqlHealthIndicator) => ({
readiness: [
() =>
mssqlIndicator.isHealthy(
'mssql://sa:password@localhost:1433/master',
),
],
}),
}),
],
})
export class AppModule {}Signature: isHealthy(url: string): Promise<HealthIndicatorResult>
Redis Health Indicator
Connects to Redis and runs a PING command.
import { Module } from '@nestjs/common';
import {
HealthCheckModule,
IORedisHealthIndicator,
} from '@eqxjs/nest-healthchecks';
@Module({
imports: [
HealthCheckModule.forRootAsync({
providers: [IORedisHealthIndicator],
inject: [IORedisHealthIndicator],
useFactory: (redisIndicator: IORedisHealthIndicator) => ({
readiness: [() => redisIndicator.isHealthy('redis://localhost:6379')],
}),
}),
],
})
export class AppModule {}Signature: isHealthy(url: string): Promise<HealthIndicatorResult>
Kafka Health Indicators
Both Kafka indicators are NestJS injectable services with Scope.TRANSIENT. They connect to their respective Kafka server singletons from the @eqxjs ecosystem and do not accept broker configuration directly.
ConfluentKafkaJS Health Indicator
Connects via the @eqxjs/kafka-server-confluent-kafkajs singleton and checks whether the Kafka connection is active.
import { Module } from '@nestjs/common';
import {
HealthCheckModule,
ConfluentKafkaJSHealthIndicator,
} from '@eqxjs/nest-healthchecks';
@Module({
imports: [
HealthCheckModule.forRootAsync({
providers: [ConfluentKafkaJSHealthIndicator],
inject: [ConfluentKafkaJSHealthIndicator],
useFactory: (kafkaIndicator: ConfluentKafkaJSHealthIndicator) => ({
readiness: [() => kafkaIndicator.checkCluster('confluent-kafkajs')],
}),
}),
],
})
export class AppModule {}Signature: checkCluster(key: string): Promise<HealthIndicatorResult>
KafkaJS Health Indicator
Connects via the @eqxjs/custom-kafka-server singleton and fetches topic metadata to verify broker connectivity.
import { Module } from '@nestjs/common';
import {
HealthCheckModule,
KafkaJsHealthIndicator,
} from '@eqxjs/nest-healthchecks';
@Module({
imports: [
HealthCheckModule.forRootAsync({
providers: [KafkaJsHealthIndicator],
inject: [KafkaJsHealthIndicator],
useFactory: (kafkaIndicator: KafkaJsHealthIndicator) => ({
readiness: [() => kafkaIndicator.checkCluster('kafka')],
}),
}),
],
})
export class AppModule {}Signature: checkCluster(key: string): Promise<HealthIndicatorResult>
Confluent Kafka Health Indicator
Connects via the @eqxjs/kafka-server-confluent-kafka singleton and checks whether the Kafka connection is active.
import { Module } from '@nestjs/common';
import {
HealthCheckModule,
ConfluentKafkaHealthIndicator,
} from '@eqxjs/nest-healthchecks';
@Module({
imports: [
HealthCheckModule.forRootAsync({
providers: [ConfluentKafkaHealthIndicator],
inject: [ConfluentKafkaHealthIndicator],
useFactory: (kafkaIndicator: ConfluentKafkaHealthIndicator) => ({
readiness: [() => kafkaIndicator.checkCluster('confluent-kafka')],
}),
}),
],
})
export class AppModule {}Signature: checkCluster(key: string): Promise<HealthIndicatorResult>
Complete Example
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import {
HealthCheckModule,
ApiHealthIndicator,
MongodbHealthIndicator,
PostgresHealthIndicator,
IORedisHealthIndicator,
KafkaJsHealthIndicator,
ConfluentKafkaJSHealthIndicator,
ConfluentKafkaHealthIndicator,
} from '@eqxjs/nest-healthchecks';
@Module({
imports: [
ConfigModule.forRoot(),
HealthCheckModule.forRootAsync({
imports: [ConfigModule],
providers: [
ApiHealthIndicator,
MongodbHealthIndicator,
PostgresHealthIndicator,
IORedisHealthIndicator,
KafkaJsHealthIndicator,
ConfluentKafkaJSHealthIndicator,
ConfluentKafkaHealthIndicator,
],
inject: [
ConfigService,
ApiHealthIndicator,
MongodbHealthIndicator,
PostgresHealthIndicator,
IORedisHealthIndicator,
KafkaJsHealthIndicator,
ConfluentKafkaJSHealthIndicator,
ConfluentKafkaHealthIndicator,
],
useFactory: (
configService: ConfigService,
apiIndicator: ApiHealthIndicator,
mongoIndicator: MongodbHealthIndicator,
postgresIndicator: PostgresHealthIndicator,
redisIndicator: IORedisHealthIndicator,
kafkaJsIndicator: KafkaJsHealthIndicator,
confluentKafkaJSIndicator: ConfluentKafkaJSHealthIndicator,
confluentKafkaIndicator: ConfluentKafkaHealthIndicator,
) => ({
readiness: [
// External API
() =>
apiIndicator.isHealthy([
{
name: 'External API',
url: configService.get('EXTERNAL_API_URL') + '/health',
},
]),
// MongoDB
() =>
mongoIndicator.isHealthy([
{
dbName: 'main',
connectionString: configService.get('MONGO_URI'),
},
]),
// PostgreSQL
() =>
postgresIndicator.isHealthy(
[
{
dbName: 'main',
connectionString: configService.get('POSTGRES_URI'),
},
],
{ timeoutMs: 2000 },
),
// Redis
() => redisIndicator.isHealthy(configService.get('REDIS_URL')),
// KafkaJS
() => kafkaJsIndicator.checkCluster('kafka'),
// Confluent KafkaJS
() => confluentKafkaJSIndicator.checkCluster('confluent-kafkajs'),
// Confluent Kafka
() => confluentKafkaIndicator.checkCluster('confluent-kafka'),
],
}),
}),
],
})
export class AppModule {}Custom Controller
Extend HealthCheckControllerBase (or HealthCheckController) to override liveness/readiness behaviour:
import { Injectable } from '@nestjs/common';
import { HealthCheckController } from '@eqxjs/nest-healthchecks';
@Injectable()
export class CustomHealthController extends HealthCheckController {
protected handleLivenessResult(result: HealthCheckResult) {
// Custom liveness response
return { status: 'ok' };
}
}
HealthCheckModule.forRoot({
controller: CustomHealthController,
indicators: { readiness: [] },
});HealthCheckControllerBase is the abstract base class that injects HealthCheckService and the config token. HealthCheckController is the default concrete implementation that formats responses into the standard envelope.
Contributing
Please see contributing.md
License
Please see LICENSE
