Framework Guide
Node.js API Monitoring
Set up health check endpoints and uptime monitoring for your Node.js application. Works with Express, Fastify, Koa, and native HTTP.
Why Monitor Your Node.js App?
Node.js applications can fail silently in production. The event loop might be blocked, database connections might pool up, or memory leaks might cause gradual degradation. External monitoring catches these issues before users notice.
- Event loop blocking -- CPU-intensive operations can freeze your app without crashing it
- Memory leaks -- Node.js apps can slowly consume memory until they OOM crash
- Dependency failures -- Database, Redis, or third-party API outages cascade through your app
- Unhandled rejections -- Uncaught promise rejections can leave your app in a broken state
Basic Health Endpoint (Native HTTP)
const http = require('http');
const server = http.createServer((req, res) => {
if (req.url === '/health' && req.method === 'GET') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
status: 'healthy',
timestamp: new Date().toISOString()
}));
return;
}
// ... your other routes
});
server.listen(3000);
Express Health Endpoint
const express = require('express');
const app = express();
// Simple liveness check
app.get('/health', (req, res) => {
res.json({ status: 'healthy' });
});
// Comprehensive readiness check
app.get('/ready', async (req, res) => {
try {
// Check database
await db.query('SELECT 1');
// Check Redis
await redis.ping();
res.json({
status: 'ready',
checks: {
database: 'ok',
redis: 'ok'
}
});
} catch (error) {
res.status(503).json({
status: 'not ready',
error: error.message
});
}
});
Fastify Health Endpoint
const fastify = require('fastify')();
fastify.get('/health', async (request, reply) => {
return { status: 'healthy' };
});
// With schema validation
fastify.get('/ready', {
schema: {
response: {
200: {
type: 'object',
properties: {
status: { type: 'string' },
uptime: { type: 'number' }
}
}
}
}
}, async (request, reply) => {
return {
status: 'ready',
uptime: process.uptime()
};
});
What to Include in Health Checks
- Database connectivity — Can you query the database?
- Cache connectivity — Is Redis/Memcached reachable?
- Memory usage — process.memoryUsage()
- Uptime — process.uptime()
- Version — Useful for debugging deployments
Complete Example with Dependencies
app.get('/health', async (req, res) => {
const health = {
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
memory: process.memoryUsage(),
version: process.env.npm_package_version || '1.0.0',
checks: {}
};
// Check database with timeout
try {
const dbStart = Date.now();
await Promise.race([
db.query('SELECT 1'),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('timeout')), 5000)
)
]);
health.checks.database = {
status: 'ok',
responseTime: Date.now() - dbStart
};
} catch (error) {
health.status = 'unhealthy';
health.checks.database = {
status: 'error',
message: error.message
};
}
const statusCode = health.status === 'healthy' ? 200 : 503;
res.status(statusCode).json(health);
});
Best Practices
- Keep it fast — Health checks should respond in under 500ms
- No auth required — Monitoring services need unauthenticated access
- Add timeouts — Don't let slow dependencies hang the health check
- Log health check traffic separately — Avoid cluttering your logs