Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
40 prioritized NestJS best practices across architecture, DI, security, performance, testing, and microservices.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
rules/micro-use-health-checks.md
1---2title: Implement Health Checks for Microservices3impact: MEDIUM-HIGH4impactDescription: Health checks enable orchestrators to manage service lifecycle5tags: microservices, health-checks, terminus, kubernetes6---78## Implement Health Checks for Microservices910Implement liveness and readiness probes using `@nestjs/terminus`. Liveness checks determine if the service should be restarted. Readiness checks determine if the service can accept traffic. Proper health checks enable Kubernetes and load balancers to route traffic correctly.1112**Incorrect (simple ping that doesn't check dependencies):**1314```typescript15// Simple ping that doesn't check dependencies16@Controller('health')17export class HealthController {18@Get()19check(): string {20return 'OK'; // Service might be unhealthy but returns OK21}22}2324// Health check that blocks on slow dependencies25@Controller('health')26export class HealthController {27@Get()28async check(): Promise<string> {29// If database is slow, health check times out30await this.userRepo.findOne({ where: { id: '1' } });31await this.redis.ping();32await this.externalApi.healthCheck();33return 'OK';34}35}36```3738**Correct (use @nestjs/terminus for comprehensive health checks):**3940```typescript41// Use @nestjs/terminus for comprehensive health checks42import {43HealthCheckService,44HttpHealthIndicator,45TypeOrmHealthIndicator,46HealthCheck,47DiskHealthIndicator,48MemoryHealthIndicator,49} from '@nestjs/terminus';5051@Controller('health')52export class HealthController {53constructor(54private health: HealthCheckService,55private http: HttpHealthIndicator,56private db: TypeOrmHealthIndicator,57private disk: DiskHealthIndicator,58private memory: MemoryHealthIndicator,59) {}6061// Liveness probe - is the service alive?62@Get('live')63@HealthCheck()64liveness() {65return this.health.check([66// Basic checks only67() => this.memory.checkHeap('memory_heap', 200 * 1024 * 1024), // 200MB68]);69}7071// Readiness probe - can the service handle traffic?72@Get('ready')73@HealthCheck()74readiness() {75return this.health.check([76() => this.db.pingCheck('database'),77() =>78this.http.pingCheck('redis', 'http://redis:6379', { timeout: 1000 }),79() =>80this.disk.checkStorage('disk', { path: '/', thresholdPercent: 0.9 }),81]);82}8384// Deep health check for debugging85@Get('deep')86@HealthCheck()87deepCheck() {88return this.health.check([89() => this.db.pingCheck('database'),90() => this.memory.checkHeap('memory_heap', 200 * 1024 * 1024),91() => this.memory.checkRSS('memory_rss', 300 * 1024 * 1024),92() =>93this.disk.checkStorage('disk', { path: '/', thresholdPercent: 0.9 }),94() =>95this.http.pingCheck('external-api', 'https://api.example.com/health'),96]);97}98}99100// Custom indicator for business-specific health101@Injectable()102export class QueueHealthIndicator extends HealthIndicator {103constructor(private queueService: QueueService) {104super();105}106107async isHealthy(key: string): Promise<HealthIndicatorResult> {108const queueStats = await this.queueService.getStats();109110const isHealthy = queueStats.failedCount < 100;111const result = this.getStatus(key, isHealthy, {112waiting: queueStats.waitingCount,113active: queueStats.activeCount,114failed: queueStats.failedCount,115});116117if (!isHealthy) {118throw new HealthCheckError('Queue unhealthy', result);119}120121return result;122}123}124125// Redis health indicator126@Injectable()127export class RedisHealthIndicator extends HealthIndicator {128constructor(@InjectRedis() private redis: Redis) {129super();130}131132async isHealthy(key: string): Promise<HealthIndicatorResult> {133try {134const pong = await this.redis.ping();135return this.getStatus(key, pong === 'PONG');136} catch (error) {137throw new HealthCheckError('Redis check failed', this.getStatus(key, false));138}139}140}141142// Use custom indicators143@Get('ready')144@HealthCheck()145readiness() {146return this.health.check([147() => this.db.pingCheck('database'),148() => this.redis.isHealthy('redis'),149() => this.queue.isHealthy('job-queue'),150]);151}152153// Graceful shutdown handling154@Injectable()155export class GracefulShutdownService implements OnApplicationShutdown {156private isShuttingDown = false;157158isShutdown(): boolean {159return this.isShuttingDown;160}161162async onApplicationShutdown(signal: string): Promise<void> {163this.isShuttingDown = true;164console.log(`Shutting down on ${signal}`);165166// Wait for in-flight requests167await new Promise((resolve) => setTimeout(resolve, 5000));168}169}170171// Health check respects shutdown state172@Get('ready')173@HealthCheck()174readiness() {175if (this.shutdownService.isShutdown()) {176throw new ServiceUnavailableException('Shutting down');177}178179return this.health.check([180() => this.db.pingCheck('database'),181]);182}183```184185### Kubernetes Configuration186187```yaml188# Kubernetes deployment with probes189apiVersion: apps/v1190kind: Deployment191metadata:192name: api-service193spec:194template:195spec:196containers:197- name: api198image: api-service:latest199ports:200- containerPort: 3000201livenessProbe:202httpGet:203path: /health/live204port: 3000205initialDelaySeconds: 30206periodSeconds: 10207timeoutSeconds: 5208failureThreshold: 3209readinessProbe:210httpGet:211path: /health/ready212port: 3000213initialDelaySeconds: 5214periodSeconds: 5215timeoutSeconds: 3216failureThreshold: 3217startupProbe:218httpGet:219path: /health/live220port: 3000221initialDelaySeconds: 0222periodSeconds: 5223failureThreshold: 30224```225226Reference: [NestJS Terminus](https://docs.nestjs.com/recipes/terminus)227