Ficus is a small, fast reactive UI library built around fine-grained reactivity and minimal overhead — signals update only the DOM nodes they touch, with no virtual DOM in the way. But that zero-overhead reactivity disappears the moment your app is unreachable or your API goes silent. 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 Ficus backend
- Vigilmon HTTP monitors for your SPA and backend API
- A heartbeat for scheduled background tasks
- Alert channels (email and Slack)
- An uptime badge in your Ficus UI
Prerequisites
- A Ficus project (Vite or custom bundler)
- Node.js backend for the health endpoint
- A free Vigilmon account
Step 1: Add a Health Endpoint
Ficus handles the frontend; your Node.js backend is where the health check lives. Add a /health route to your server:
// server.js (Express)
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;
}
// External dependency check
try {
const resp = await fetch("https://api.yourservice.com/ping", {
signal: AbortSignal.timeout(3000),
});
checks.upstream = resp.ok ? "ok" : `http_${resp.status}`;
} catch {
checks.upstream = "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-07-03T...","checks":{"database":"ok","upstream":"ok"}}
Step 2: Configure Vigilmon HTTP Monitors
Log in to Vigilmon → Monitors → New Monitor:
Monitor 1: Ficus SPA
| Field | Value |
|---|---|
| URL | https://yourapp.com/ |
| Method | GET |
| Expected status | 200 |
| Expected body | A stable string from your HTML (e.g. app title or <div id="app">) |
| Check interval | 1 minute |
Monitor 2: Backend Health API
| Field | Value |
|---|---|
| URL | https://yourapp.com/health |
| Method | GET |
| Expected status | 200 |
| Check interval | 1 minute |
Two monitors cover two independent failure classes: static asset serving and the backend API layer.
Step 3: Heartbeat for Background Tasks
Background jobs — cache rehydration, notification delivery, data aggregation — 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
}
}
// Data aggregation — runs every 5 minutes
cron.schedule("*/5 * * * *", async () => {
try {
await aggregateMetrics();
await pingHeartbeat();
} catch (err) {
console.error("[scheduler] Aggregation failed:", err);
}
});
async function aggregateMetrics() {
// Your actual aggregation logic
}
Set up the heartbeat in Vigilmon:
- Monitors → New Heartbeat Monitor
- Set Expected interval to 10 minutes (2× the cron interval)
- Copy the ping URL to your
.env:
VIGILMON_HEARTBEAT_URL=https://vigilmon.online/api/heartbeat/xxxxxxxx
Step 4: Display Uptime Status in Your Ficus UI
Ficus's fine-grained reactivity makes the badge component extremely cheap — only the <img> node reacts to the load event:
// src/components/StatusBadge.js
import { signal, effect } from "ficus"; // adjust import to your Ficus version
const BADGE_URL = "https://vigilmon.online/api/badge/your-monitor-id.svg";
const STATUS_URL = "https://vigilmon.online/status/your-monitor-slug";
export function StatusBadge(container) {
const loaded = signal(false);
const anchor = document.createElement("a");
anchor.href = STATUS_URL;
anchor.target = "_blank";
anchor.rel = "noopener noreferrer";
anchor.setAttribute("aria-label", "Service uptime status");
const img = document.createElement("img");
img.src = BADGE_URL;
img.alt = "Uptime";
img.width = 120;
img.height = 20;
img.addEventListener("load", () => loaded.set(true));
effect(() => {
img.style.opacity = loaded.get() ? "1" : "0";
img.style.transition = "opacity 0.2s";
});
anchor.appendChild(img);
container.appendChild(anchor);
}
Mount it in your app entry point:
// src/main.js
import { StatusBadge } from "./components/StatusBadge.js";
const footer = document.getElementById("footer");
if (footer) StatusBadge(footer);
Step 5: Set Up Alert Channels
In Vigilmon's Alert Channels settings:
- Alert Channels → Email → add your on-call email
- Attach to both HTTP monitors and the heartbeat
Slack
- Create a Slack Incoming Webhook for
#alerts - Alert Channels → Webhook → paste the URL
- 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
curl -s https://yourapp.com/health | jq .
# 2. Kill the DB connection → confirm health returns 503
# Expected: Vigilmon alerts within 2 minutes
# 3. Remove VIGILMON_HEARTBEAT_URL and 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 SPA check | CDN failures, broken static deploys | | HTTP health API | Database, cache, and upstream API failures | | Heartbeat | Scheduler crashes, silent aggregation failures |
Next Steps
- Create separate monitors for staging vs. production
- Use Vigilmon's response time graphs to spot API latency regressions
- 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.