DEV Community

Vigilmon
Vigilmon

Posted on

How to Monitor Your Express.js Application with Vigilmon (Health Checks + Uptime)

Express.js is the most popular Node.js web framework — millions of apps are built on it. But it has no built-in health check mechanism, and it gives you no warnings when it is struggling under load or when a dependency fails.

This guide shows how to add external uptime monitoring to your Express.js application with Vigilmon.

Step 1: Add a Health Check Route

Express makes it simple to add a /health endpoint:

const express = require("express");
const app = express();

app.get("/health", (req, res) => {
  res.json({ status: "ok" });
});

app.listen(3000);
Enter fullscreen mode Exit fullscreen mode

For a more comprehensive health check that verifies your dependencies:

const { Pool } = require("pg");
const pool = new Pool({ connectionString: process.env.DATABASE_URL });

app.get("/health", async (req, res) => {
  const checks = {};

  // Check PostgreSQL
  try {
    await pool.query("SELECT 1");
    checks.database = "ok";
  } catch (e) {
    checks.database = `error: ${e.message}`;
  }

  // Check Redis if applicable
  if (redisClient) {
    try {
      await redisClient.ping();
      checks.cache = "ok";
    } catch (e) {
      checks.cache = "error";
    }
  }

  const isHealthy = Object.values(checks).every(v => v === "ok");
  res.status(isHealthy ? 200 : 503).json({
    status: isHealthy ? "ok" : "degraded",
    checks,
  });
});
Enter fullscreen mode Exit fullscreen mode

Vigilmon treats any non-2xx response as a failure. Return 503 when degraded — this triggers the alert automatically.

Step 2: Protect the Health Endpoint

If your health endpoint exposes sensitive dependency information, protect it:

const MONITORING_TOKEN = process.env.MONITORING_TOKEN;

app.get("/health/detailed", (req, res) => {
  const token = req.headers.authorization?.split("Bearer ")[1];
  if (token !== MONITORING_TOKEN) {
    return res.status(403).json({ error: "Forbidden" });
  }

  // Return detailed health info
  res.json({ status: "ok", database: "ok", memory: process.memoryUsage() });
});
Enter fullscreen mode Exit fullscreen mode

Configure Vigilmon to send Authorization: Bearer your-monitoring-token with each request.

Step 3: Track Process Health

Express does not automatically restart when it runs out of memory or when the event loop is blocked. Add metrics to your health endpoint:

app.get("/health", (req, res) => {
  const mem = process.memoryUsage();
  const heapUsedMB = Math.round(mem.heapUsed / 1024 / 1024);
  const heapTotalMB = Math.round(mem.heapTotal / 1024 / 1024);
  const heapPercent = Math.round((mem.heapUsed / mem.heapTotal) * 100);

  // Alert if memory is over 90%
  const memStatus = heapPercent > 90 ? "warning" : "ok";

  res.json({
    status: memStatus === "ok" ? "ok" : "degraded",
    checks: { memory: memStatus },
    metrics: {
      heapUsedMB,
      heapTotalMB,
      heapPercent,
      uptime: Math.round(process.uptime()),
    },
  });
});
Enter fullscreen mode Exit fullscreen mode

Step 4: Set Up Vigilmon

  1. Sign up at vigilmon.online — free for 50 monitors, 1-minute checks
  2. Add your Express health endpoint
  3. Set check regions: pick 2-3 closest to your users
  4. Set timeout to match your slowest acceptable response time
  5. Connect Slack or email for alerts

Handling Multiple Express Instances

If you run multiple Express instances behind a load balancer, monitor:

  1. The load balancer URL — this tests the full stack
  2. Individual instance health — if your load balancer exposes instance endpoints

A load balancer can route traffic to a broken instance while others are fine. Monitoring only the LB URL misses per-instance failures.

Express.js with PM2

If you use PM2 to run Express in production, combine PM2 cluster management with Vigilmon external monitoring:

// ecosystem.config.js
module.exports = {
  apps: [{
    name: "my-api",
    script: "app.js",
    instances: "max",
    exec_mode: "cluster",
    max_memory_restart: "1G",  // PM2 restarts if over 1GB
  }]
};
Enter fullscreen mode Exit fullscreen mode

PM2 restarts unhealthy instances. Vigilmon alerts you when the overall service is unreachable from the outside — which can happen even when PM2 shows all instances as online.

TypeScript and Express

For TypeScript Express apps:

import express, { Request, Response } from "express";
import { Pool } from "pg";

const app = express();
const pool = new Pool({ connectionString: process.env.DATABASE_URL });

interface HealthResponse {
  status: "ok" | "degraded";
  checks: Record<string, string>;
}

app.get("/health", async (req: Request, res: Response<HealthResponse>) => {
  const checks: Record<string, string> = {};

  try {
    await pool.query("SELECT 1");
    checks.database = "ok";
  } catch (e) {
    checks.database = "error";
  }

  const isHealthy = Object.values(checks).every(v => v === "ok");
  res.status(isHealthy ? 200 : 503).json({
    status: isHealthy ? "ok" : "degraded",
    checks,
  });
});

app.listen(3000, () => console.log("Server running on port 3000"));
Enter fullscreen mode Exit fullscreen mode

Common Express Failure Modes

  1. Unhandled promise rejections — crash the process in newer Node.js versions
  2. Memory leaks — heap grows until OOM kill
  3. Event loop blocking — synchronous operations block all requests
  4. Database connection pool exhaustion — new requests queue indefinitely
  5. Dependency failures — upstream APIs time out, cascading to your responses

Add process.on("unhandledRejection", ...) to catch unhandled rejections, and monitor externally with Vigilmon to catch anything that slips through.

Add uptime monitoring to your Express app at vigilmon.online — free, no credit card, 5 minutes to set up.

Top comments (0)