tutorial

Monitoring Your Neo.mjs App with Vigilmon: Health Checks, Heartbeats & Uptime Alerts

Add production-grade monitoring to a Neo.mjs application — a health API, background job heartbeats, and instant alerts when your multi-threaded UI framework or backend goes down.

Neo.mjs is a unique application worker UI framework that moves most of the application logic off the main browser thread using Web Workers, delivering exceptional UI performance. But that multi-threaded performance advantage disappears the moment your app is down, your API is unreachable, or a worker task is silently crashing. Vigilmon gives you the visibility layer you need: continuous HTTP monitoring, heartbeat checks, and instant alerts routed to email or Slack.

What You'll Build

  • A health API endpoint for your Neo.mjs backend
  • Vigilmon HTTP monitors for your Neo.mjs app and backend
  • A heartbeat for scheduled background tasks
  • Alert channels (email and Slack)
  • An uptime badge embedded in your Neo.mjs UI

Prerequisites

  • A Neo.mjs project (npx neo-app)
  • Node.js backend for the health endpoint
  • A free Vigilmon account

Step 1: Add a Health Endpoint

Neo.mjs apps typically pair with a Node.js/Express backend. Add a /health route:

// server.js
import express from "express";

const app = express();

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

  // Database check
  try {
    // await db.query("SELECT 1");
    checks.database = "ok";
  } catch (err) {
    checks.database = `error: ${err.message}`;
    degraded = true;
  }

  // Worker service check — verify the Neo.mjs worker host is reachable
  try {
    const r = await fetch("http://localhost:8080/ping", {
      signal: AbortSignal.timeout(3000),
    });
    checks.workerHost = r.ok ? "ok" : `http_${r.status}`;
  } catch {
    checks.workerHost = "unreachable";
    degraded = true;
  }

  res.status(degraded ? 503 : 200).json({
    status: degraded ? "degraded" : "ok",
    timestamp: new Date().toISOString(),
    checks,
  });
});

app.listen(process.env.PORT ?? 3000);

Test it:

curl http://localhost:3000/health | jq .
# {"status":"ok","timestamp":"2026-06-29T...","checks":{"database":"ok","workerHost":"ok"}}

Step 2: Configure Vigilmon HTTP Monitors

Log in to VigilmonMonitors → New Monitor:

Monitor 1: Neo.mjs App Shell

| Field | Value | |---|---| | URL | https://yourapp.com/ | | Method | GET | | Expected status | 200 | | Expected body | A stable string from your HTML shell | | Check interval | 1 minute |

Monitor 2: Health API

| Field | Value | |---|---| | URL | https://yourapp.com/health | | Method | GET | | Expected status | 200 | | Check interval | 1 minute |

With both monitors, you cover two independent failure classes: the static hosting layer and the backend/data layer. Neo.mjs's worker-based architecture means the app shell can load even when the worker host is unhealthy — monitoring both independently catches this.


Step 3: Heartbeat for Background Tasks

Background jobs — data prefetch, worker cache invalidation, session cleanup — can fail silently. A Vigilmon heartbeat catches this by alerting when the expected ping doesn't arrive.

npm install node-cron
// scheduler.js
import cron from "node-cron";

const VIGILMON_HEARTBEAT_URL = process.env.VIGILMON_HEARTBEAT_URL ?? "";

async function pingHeartbeat() {
  if (!VIGILMON_HEARTBEAT_URL) return;
  try {
    await fetch(VIGILMON_HEARTBEAT_URL, { signal: AbortSignal.timeout(5000) });
  } catch {
    // Monitoring must never crash the job
  }
}

cron.schedule("*/15 * * * *", async () => {
  try {
    await invalidateWorkerCache();
    await pingHeartbeat();
  } catch (err) {
    console.error("[scheduler] Cache invalidation failed:", err);
    // Vigilmon alerts after the heartbeat window expires with no ping
  }
});

async function invalidateWorkerCache() {
  // Your actual cache invalidation logic
}

Set up the heartbeat in Vigilmon:

  1. Monitors → New Heartbeat Monitor
  2. Set Expected interval to 30 minutes (2× the cron interval for a grace window)
  3. Copy the ping URL to your .env:
VIGILMON_HEARTBEAT_URL=https://vigilmon.online/api/heartbeat/xxxxxxxx

Step 4: Display Uptime Status in Your Neo.mjs UI

Neo.mjs components extend Neo.component.Base. Add a status badge component that runs safely on the main thread:

// apps/myapp/view/StatusBadge.mjs
import Component from '../../../node_modules/neo.mjs/src/component/Base.mjs';

class StatusBadge extends Component {
  static config = {
    className: 'MyApp.view.StatusBadge',

    badgeUrl_: 'https://vigilmon.online/api/badge/your-monitor-id.svg',
    statusUrl_: 'https://vigilmon.online/status/your-monitor-slug',

    tag: 'a',

    domListeners: {},
  };

  getVdomRoot() {
    return {
      tag: 'a',
      href: this.statusUrl,
      target: '_blank',
      rel: 'noopener noreferrer',
      'aria-label': 'Service uptime status',
      cn: [
        {
          tag: 'img',
          src: this.badgeUrl,
          alt: 'Uptime',
          width: 120,
          height: 20,
        },
      ],
    };
  }
}

Neo.applyClassConfig(StatusBadge);
export default StatusBadge;

Add it to your main viewport:

// apps/myapp/view/MainContainer.mjs
import Container from '../../../node_modules/neo.mjs/src/container/Base.mjs';
import StatusBadge from './StatusBadge.mjs';

class MainContainer extends Container {
  static config = {
    className: 'MyApp.view.MainContainer',
    layout: { ntype: 'vbox', align: 'stretch' },
    items: [
      // ... your main content
      { module: StatusBadge },
    ],
  };
}

Neo.applyClassConfig(MainContainer);
export default MainContainer;

Step 5: Set Up Alert Channels

In Vigilmon's Alert Channels settings:

Email

  1. Alert Channels → Email → add your on-call email
  2. Attach to both HTTP monitors and the heartbeat

Slack

  1. Create a Slack Incoming Webhook for #alerts
  2. Alert Channels → Webhook → paste the URL
  3. Payload template:
{
  "text": "🚨 *{{ monitor.name }}* is DOWN\n{{ monitor.url }}\nStatus: {{ event.status }}\n<https://vigilmon.online|Open Vigilmon>"
}

Step 6: Verify Everything Works

# 1. Health endpoint responds correctly
curl -s https://yourapp.com/health | jq .

# 2. Simulate DB failure → confirm health returns 503
# Expected: Vigilmon alerts within 2 minutes

# 3. Remove VIGILMON_HEARTBEAT_URL → let the scheduler run
# Expected: heartbeat alert fires after the grace window

# 4. Vigilmon → "Test Alert" → confirm delivery to Slack/email

Summary

| Monitor | What It Catches | |---|---| | HTTP app shell check | Hosting failures, broken static deploys | | HTTP health API | Database and worker host failures | | Heartbeat | Scheduler crashes, silent cache/sync failures |


Next Steps

  • Create separate monitors for staging vs. production
  • Use Vigilmon's response time graphs to detect latency regressions caused by worker overhead
  • Configure multi-channel alerting: email for low-severity, Slack for critical

Ready to get started? Sign up for Vigilmon free — have your first monitor running in under 5 minutes.

Monitor your app with Vigilmon

Free plan — 5 monitors, no credit card required. Up and running in 60 seconds.

Start free →