Framework Guide
Express API Monitoring
Set up health check endpoints and uptime monitoring for your Express.js application. Production-ready patterns for API monitoring.
Simple Health Endpoint
const express = require('express');
const app = express();
app.get('/health', (req, res) => {
res.json({
status: 'healthy',
timestamp: new Date().toISOString()
});
});
Comprehensive Health Check
const express = require('express');
const app = express();
app.get('/health', (req, res) => {
res.json({ status: 'healthy' });
});
app.get('/ready', async (req, res) => {
const checks = {};
let healthy = true;
// Check database
try {
const start = Date.now();
await db.query('SELECT 1');
checks.database = {
status: 'ok',
responseTimeMs: Date.now() - start
};
} catch (error) {
healthy = false;
checks.database = {
status: 'error',
message: error.message
};
}
// Check Redis
try {
const start = Date.now();
await redis.ping();
checks.redis = {
status: 'ok',
responseTimeMs: Date.now() - start
};
} catch (error) {
healthy = false;
checks.redis = {
status: 'error',
message: error.message
};
}
const statusCode = healthy ? 200 : 503;
res.status(statusCode).json({
status: healthy ? 'healthy' : 'unhealthy',
timestamp: new Date().toISOString(),
checks,
uptime: process.uptime(),
memory: process.memoryUsage()
});
});
Health Check Router
// routes/health.js
const express = require('express');
const router = express.Router();
// Liveness probe
router.get('/live', (req, res) => {
res.json({ status: 'alive' });
});
// Readiness probe
router.get('/ready', async (req, res) => {
try {
await checkDependencies();
res.json({ status: 'ready' });
} catch (error) {
res.status(503).json({
status: 'not ready',
error: error.message
});
}
});
// Detailed health
router.get('/detailed', async (req, res) => {
const health = await getDetailedHealth();
res.status(health.healthy ? 200 : 503).json(health);
});
module.exports = router;
// app.js
const healthRouter = require('./routes/health');
app.use('/health', healthRouter);
Using express-healthcheck
const healthcheck = require('express-healthcheck');
app.use('/health', healthcheck({
healthy: function () {
return { everything: 'is ok' };
}
}));
// Custom checks
app.use('/health', healthcheck({
test: async function () {
// Throw error if unhealthy
await db.query('SELECT 1');
await redis.ping();
}
}));
Add Timeouts
async function checkWithTimeout(fn, timeoutMs = 5000) {
return Promise.race([
fn(),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('timeout')), timeoutMs)
)
]);
}
app.get('/ready', async (req, res) => {
try {
await checkWithTimeout(() => db.query('SELECT 1'), 5000);
await checkWithTimeout(() => redis.ping(), 2000);
res.json({ status: 'ready' });
} catch (error) {
res.status(503).json({
status: 'not ready',
error: error.message
});
}
});
Skip Logging for Health Checks
const morgan = require('morgan');
// Skip health check logging
app.use(morgan('combined', {
skip: (req, res) => req.path === '/health' || req.path === '/ready'
}));
Best Practices
- Separate liveness and readiness — /health/live and /health/ready serve different purposes
- Add timeouts — Don't let slow dependencies hang the health check
- Skip logging — Avoid cluttering logs with health check traffic
- Include process info — Uptime, memory usage, and version help debugging