Health Checks
Guren provides a comprehensive health checking system to monitor your application's dependencies and services. Use health checks to expose a /health endpoint for load balancers, orchestrators, and monitoring tools.
Health Checks
Guren provides a comprehensive health checking system to monitor your application's dependencies and services. Use health checks to expose a /health endpoint for load balancers, orchestrators, and monitoring tools.
Configuration
Create a health manager and register checks:
import { createHealthManager, DatabaseCheck, RedisCheck, MemoryCheck } from '@guren/core'
const health = createHealthManager()
// Register checks
health.register(new DatabaseCheck(db))
health.register(new RedisCheck(redis))
health.register(new MemoryCheck({ thresholdMb: 512 }))
// Run all checks
const report = await health.check()
console.log(report.status) // 'healthy', 'degraded', or 'unhealthy'
Built-in Checks
Database Check
Verifies database connectivity by executing a simple query:
import { DatabaseCheck } from '@guren/core'
// Basic usage
health.register(new DatabaseCheck(db))
// With custom options
health.register(new DatabaseCheck(db, {
name: 'mysql', // Custom name (default: 'database')
query: 'SELECT 1 + 1', // Custom query (default: 'SELECT 1')
}))
Redis Check
Verifies Redis connectivity:
import { RedisCheck } from '@guren/core'
// Basic usage
health.register(new RedisCheck(redis))
// With custom options
health.register(new RedisCheck(redis, {
name: 'redis-cache', // Custom name (default: 'redis')
}))
Memory Check
Monitors process memory usage with configurable thresholds:
import { MemoryCheck } from '@guren/core'
health.register(new MemoryCheck({
name: 'memory', // Check name (default: 'memory')
thresholdMb: 512, // Warning threshold (default: 512)
criticalThresholdMb: 1024, // Critical threshold (default: 1024)
}))
Memory check returns status based on heap usage:
- healthy: Below threshold
- degraded: Above threshold, below critical
- unhealthy: Above critical threshold
Cache Check
Verifies cache store connectivity:
import { CacheCheck } from '@guren/core'
health.register(new CacheCheck(cache, {
name: 'cache', // Custom name (default: 'cache')
}))
Storage Check
Verifies storage driver connectivity:
import { StorageCheck } from '@guren/core'
health.register(new StorageCheck(storage, {
name: 'storage', // Custom name (default: 'storage')
disk: 'local', // Disk to check (default: default disk)
testPath: '.health', // Test file path (default: '.health')
}))
Custom Checks
Using CustomCheck
Create checks with a callback function:
import { customCheck } from '@guren/core'
// Simple custom check
health.register(customCheck('external-api', async () => {
try {
const response = await fetch('https://api.example.com/health')
if (response.ok) {
return { status: 'healthy', message: 'API is responding' }
}
return { status: 'degraded', message: 'API returned error' }
} catch (error) {
return { status: 'unhealthy', message: 'API is unreachable' }
}
}))
// With metadata
health.register(customCheck('queue-depth', async () => {
const depth = await getQueueDepth()
return {
status: depth < 1000 ? 'healthy' : 'degraded',
message: `Queue has ${depth} jobs`,
meta: { depth, maxDepth: 1000 },
}
}))
Extending HealthCheck Class
For reusable checks, extend the base class:
import { HealthCheck, CheckResult } from '@guren/core'
export class ExternalServiceCheck extends HealthCheck {
readonly name: string
constructor(
private url: string,
private serviceName: string
) {
super()
this.name = serviceName
}
async check(): Promise<CheckResult> {
try {
const start = Date.now()
const response = await fetch(this.url)
const latency = Date.now() - start
if (!response.ok) {
return this.degraded(`Service returned ${response.status}`, { latency })
}
if (latency > 1000) {
return this.degraded('Service responding slowly', { latency })
}
return this.healthy('Service is healthy', { latency })
} catch (error) {
return this.unhealthy(
error instanceof Error ? error.message : 'Service unreachable'
)
}
}
}
// Usage
health.register(new ExternalServiceCheck(
'https://api.stripe.com/health',
'stripe'
))
Check Options
When registering checks, you can configure options:
health.register(check, {
timeout: 5000, // Timeout in ms (default: 5000)
critical: true, // If true, failure marks overall as unhealthy
})
Critical vs Non-Critical Checks
- Critical checks: If unhealthy, the overall status becomes "unhealthy"
- Non-critical checks: If unhealthy, the overall status becomes "degraded"
// Database is critical - app can't function without it
health.register(new DatabaseCheck(db), { critical: true })
// Cache is nice-to-have - app can still function
health.register(new RedisCheck(redis), { critical: false })
Health Report
The health check returns a report object:
const report = await health.check()
console.log(report)
// {
// status: 'healthy' | 'degraded' | 'unhealthy',
// timestamp: Date,
// checks: [
// {
// name: 'database',
// status: 'healthy',
// message: 'Database connection is healthy',
// duration: 12,
// meta: { ... }
// },
// // ...
// ]
// }
Running Specific Checks
// Run only specific checks
const report = await health.checkOnly(['database', 'redis'])
// Get a single check result
const dbResult = await health.getCheck('database')
HTTP Middleware
Use the built-in middleware to expose a health endpoint:
import { createHealthManager } from '@guren/core'
const health = createHealthManager()
// ... register checks
// In your routes
router.get('/health', health.middleware())
// With options
router.get('/health', health.middleware({
detailed: true, // Include check details (default: true)
checks: ['database'] // Only run specific checks
}))
// Simple endpoint (just status)
router.get('/health/simple', health.middleware({ detailed: false }))
Response Format
Detailed response (200 OK for healthy/degraded, 503 for unhealthy):
{
"status": "healthy",
"timestamp": "2024-01-15T10:30:00.000Z",
"checks": [
{
"name": "database",
"status": "healthy",
"message": "Database connection is healthy",
"duration": 12,
"meta": {}
},
{
"name": "memory",
"status": "healthy",
"message": "Memory usage normal: 256MB",
"duration": 1,
"meta": {
"usedMb": 256,
"totalMb": 512,
"thresholdMb": 512
}
}
]
}
Simple response:
{
"status": "healthy",
"timestamp": "2024-01-15T10:30:00.000Z"
}
CLI Command
Run Health Checks
Execute health checks from the command line:
# Run all health checks
bunx guren health:check
# Run specific checks
bunx guren health:check --checks database,redis
# Output as JSON
bunx guren health:check --json
Output:
Health Check Report
==================
Status: healthy
Timestamp: 2024-01-15T10:30:00.000Z
Checks:
[OK] database (12ms)
Database connection is healthy
[OK] memory (1ms)
Memory usage normal: 256MB
[OK] redis (5ms)
Redis connection is healthy
Overall: 3/3 checks passing
Integration Example
Complete Setup
// app/health.ts
import {
createHealthManager,
DatabaseCheck,
RedisCheck,
MemoryCheck,
customCheck,
} from '@guren/core'
import { db } from './database'
import { redis } from './redis'
export const health = createHealthManager()
// Critical checks
health.register(new DatabaseCheck(db), { critical: true })
// Non-critical checks
health.register(new RedisCheck(redis), { critical: false })
health.register(new MemoryCheck({
thresholdMb: 512,
criticalThresholdMb: 1024,
}))
// Custom check for external API
health.register(customCheck('payment-api', async () => {
const response = await fetch('https://api.stripe.com/health')
return {
status: response.ok ? 'healthy' : 'degraded',
message: response.ok ? 'Payment API available' : 'Payment API issues',
}
}), { critical: false })
// routes/api.ts
import { health } from '../app/health'
router.get('/health', health.middleware())
router.get('/health/live', health.middleware({ detailed: false }))
router.get('/health/ready', health.middleware({
checks: ['database'],
detailed: false,
}))
Best Practices
- Separate liveness from readiness - Use
/health/livefor basic checks and/health/readyfor full checks - Mark database as critical - Your app usually can't function without it
- Keep checks fast - Set appropriate timeouts, avoid slow checks
- Include metadata - Help debugging by including relevant metrics
- Use for orchestrators - Kubernetes/Docker can use health endpoints for container health
- Monitor degraded state - Set up alerts for degraded status before it becomes unhealthy