Analog.js is the full-stack Angular meta-framework — it brings file-based routing, server-side rendering, and API routes to Angular, much like Next.js does for React. When you run Analog in production, you get a Node.js server you need to monitor.
This tutorial walks through adding uptime monitoring to an Analog.js application using Vigilmon. We will cover:
- A typed health API route in Analog
- HTTP monitoring in Vigilmon with keyword assertion
- Webhook alerting on DOWN/UP transitions
Prerequisites
- Node.js 18+
- An Analog.js project (
npm create analog@latest) - A free account at vigilmon.online
Part 1: Add a health API route
Analog uses Nitro under the hood for its server, so API routes live in src/server/routes/ and are plain TypeScript files.
Create src/server/routes/health.get.ts:
// src/server/routes/health.get.ts
import { defineEventHandler, setResponseStatus } from 'h3';
interface HealthCheck {
name: string;
status: 'ok' | 'error';
message?: string;
}
interface HealthResponse {
status: 'ok' | 'degraded';
timestamp: string;
uptime: number;
checks: HealthCheck[];
}
export default defineEventHandler(async (event) => {
const checks: HealthCheck[] = [];
let degraded = false;
// Example: check an environment variable is present (replace with real DB ping)
if (process.env['DATABASE_URL']) {
checks.push({ name: 'database_config', status: 'ok' });
} else {
checks.push({ name: 'database_config', status: 'error', message: 'DATABASE_URL not set' });
degraded = true;
}
const status: HealthResponse['status'] = degraded ? 'degraded' : 'ok';
const httpStatus = degraded ? 503 : 200;
setResponseStatus(event, httpStatus);
return {
status,
timestamp: new Date().toISOString(),
uptime: process.uptime(),
checks,
} satisfies HealthResponse;
});
The filename convention health.get.ts makes Analog/Nitro expose this handler at GET /api/health. Verify locally:
npm run dev
curl http://localhost:5173/api/health
Expected output:
{
"status": "ok",
"timestamp": "2026-06-30T10:00:00.000Z",
"uptime": 12.3,
"checks": [
{ "name": "database_config", "status": "ok" }
]
}
If any check fails, the route returns HTTP 503 — which Vigilmon treats as an outage.
Skipping Angular's SSR middleware for the health route
Analog runs Angular SSR on the server. Make sure your health route is resolved by Nitro before Angular's catch-all SSR handler. Because Analog places API routes under /api/ and Angular handles everything else, this works automatically without additional configuration.
Part 2: Set up HTTP monitoring in Vigilmon
- Log in to vigilmon.online and click Add Monitor.
- Choose HTTP(S) monitor.
- URL:
https://yourapp.example.com/api/health - Interval: 1 minute.
- Under Keyword assertion, enter
"status":"ok"— Vigilmon will fail the check if this string is absent from the response body, even if the HTTP status is 200. - Add your alert channel (email, Slack, or webhook URL).
- Click Save.
The keyword check catches the "degraded" case where your server responds 200 but an internal dependency has failed — a common pattern when load balancers absorb the HTTP status but pass through the application body.
Part 3: Receive Vigilmon webhooks
Add a webhook handler to fan out DOWN/UP alerts to Slack or your on-call system.
Create src/server/routes/webhook/vigilmon.post.ts:
// src/server/routes/webhook/vigilmon.post.ts
import { defineEventHandler, readBody } from 'h3';
interface VigilmonPayload {
monitor_name: string;
status: 'down' | 'up';
url: string;
response_code: number;
checked_at: string;
}
export default defineEventHandler(async (event) => {
const body = await readBody<VigilmonPayload>(event);
if (body.status === 'down') {
console.error('[VIGILMON] Monitor DOWN', {
monitor: body.monitor_name,
url: body.url,
code: body.response_code,
at: body.checked_at,
});
const slackUrl = process.env['SLACK_WEBHOOK_URL'];
if (slackUrl) {
await fetch(slackUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
text: `*ALERT*: ${body.monitor_name} is DOWN\nURL: ${body.url}\nCode: ${body.response_code}`,
}),
});
}
} else {
console.info('[VIGILMON] Monitor recovered', { monitor: body.monitor_name });
}
return { received: true };
});
In Vigilmon, add a Webhook alert channel pointing at https://yourapp.example.com/api/webhook/vigilmon.
Part 4: Heartbeat monitoring for background tasks
If your Analog application runs background jobs (cron tasks via a separate worker, queues), use a Vigilmon heartbeat monitor so silence itself triggers an alert.
- In Vigilmon, click Add Monitor → Heartbeat.
- Set expected interval to match your task frequency (e.g., 10 minutes).
- Copy the heartbeat URL:
https://vigilmon.online/heartbeat/your-unique-token
Ping it at the end of each successful task run:
// src/server/utils/heartbeat.ts
export async function pingHeartbeat(): Promise<void> {
const url = process.env['VIGILMON_HEARTBEAT_URL'];
if (!url) return;
try {
await fetch(url, { signal: AbortSignal.timeout(5_000) });
} catch (err) {
console.warn('Heartbeat ping failed:', err);
}
}
// In your background task / Nitro scheduled task
import { pingHeartbeat } from '../utils/heartbeat';
export default defineNitroPlugin((nitroApp) => {
// Run a task every 10 minutes
setInterval(async () => {
try {
await runMyBackgroundTask();
await pingHeartbeat();
} catch (err) {
console.error('Background task failed:', err);
// No heartbeat ping — Vigilmon alerts after the expected interval passes
}
}, 10 * 60 * 1000);
});
Deployment checklist
- Set
DATABASE_URLand other env vars in your hosting environment. - Set
VIGILMON_HEARTBEAT_URLif you use heartbeat monitoring. - Optionally set
SLACK_WEBHOOK_URLfor the webhook handler. - Confirm
GET /api/healthis publicly reachable (not behind auth middleware). - Verify Vigilmon shows UP within two check intervals after deployment.
Summary
| What to monitor | How |
|---|---|
| App server availability | HTTP monitor on /api/health |
| Internal dependency health | Keyword assertion on "status":"ok" |
| Background task execution | Heartbeat monitor with pingHeartbeat() |
| Alerts to your team | Webhook or Slack alert channel |
Analog.js gives you a clean TypeScript-first server layer — pairing it with Vigilmon adds the external monitoring layer that tells you what your users experience, not just what your logs say.