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

Monitor your Express API

Add your health endpoint to UptimeSignal and get alerted when it fails.

Start monitoring free →

More Framework Guides