Alpine.js sprinkles interactivity directly into your HTML — no build step, no bundler, just script tags and x-data. But beneath that simplicity your server is still fielding fetch requests, and if it goes down your Alpine components silently stop responding. Vigilmon monitors your Alpine.js server and its endpoints continuously so you find out about outages before users notice frozen UI.
What You'll Build
- A health check Alpine component that probes your API from the browser
- Vigilmon HTTP monitors for your server and key API endpoints
- A cron heartbeat for background jobs
- A global error hook that fires a Vigilmon webhook on unhandled errors
- Email and Slack alert channels
Prerequisites
- An Alpine.js app served from any backend (Laravel, Rails, Django, Node, Go, etc.)
- A backend API endpoint your Alpine components call
- A free Vigilmon account
Step 1: Create a Health Check Alpine Component
Alpine's x-data and x-init make it straightforward to run a health check and surface the result to users.
<!-- health-check.html -->
<div
x-data="{
healthy: null,
latencyMs: null,
error: null,
async check() {
const start = Date.now();
try {
const res = await fetch('/health', { signal: AbortSignal.timeout(5000) });
this.latencyMs = Date.now() - start;
if (!res.ok) throw new Error('HTTP ' + res.status);
this.healthy = true;
this.error = null;
} catch (err) {
this.healthy = false;
this.latencyMs = null;
this.error = err.message;
}
}
}"
x-init="check(); setInterval(() => check(), 60000)"
>
<template x-if="healthy === false">
<div class="alert alert-error">
API error: <span x-text="error"></span>
</div>
</template>
<template x-if="healthy === true">
<div class="status-ok">
API healthy · <span x-text="latencyMs"></span>ms
</div>
</template>
</div>
Include this component in your layout so it appears on every page and gives users early warning of degradation.
Step 2: Add a /health Endpoint to Your Backend
Vigilmon needs a stable URL to probe from outside. Add a /health route to your server.
Laravel (PHP)
// routes/web.php
Route::get('/health', function () {
$checks = [];
$degraded = false;
try {
DB::select('SELECT 1');
$checks['database'] = 'ok';
} catch (\Exception $e) {
$checks['database'] = 'error: ' . $e->getMessage();
$degraded = true;
}
return response()->json([
'status' => $degraded ? 'degraded' : 'ok',
'checks' => $checks,
'timestamp' => now()->toIso8601String(),
], $degraded ? 503 : 200);
});
Rails (Ruby)
# config/routes.rb
get '/health', to: 'health#show'
# app/controllers/health_controller.rb
class HealthController < ApplicationController
def show
checks = {}
degraded = false
begin
ActiveRecord::Base.connection.execute('SELECT 1')
checks[:database] = 'ok'
rescue => e
checks[:database] = "error: #{e.message}"
degraded = true
end
status = degraded ? :service_unavailable : :ok
render json: { status: degraded ? 'degraded' : 'ok', checks:, timestamp: Time.now.iso8601 }, status:
end
end
Node.js / Express
app.get("/health", async (req, res) => {
const checks = {};
let degraded = false;
try {
await db.query("SELECT 1");
checks.database = "ok";
} catch (err) {
checks.database = `error: ${err.message}`;
degraded = true;
}
res.status(degraded ? 503 : 200).json({
status: degraded ? "degraded" : "ok",
checks,
timestamp: new Date().toISOString(),
});
});
Step 3: Add Vigilmon HTTP Monitors
Monitor 1: Server Uptime
| Field | Value |
|---|---|
| URL | https://yourdomain.com/health |
| Method | GET |
| Expected status | 200 |
| Expected body | "status":"ok" |
| Check interval | 1 minute |
Monitor 2: Key API Endpoint
Monitor the endpoint your Alpine components depend on most heavily.
| Field | Value |
|---|---|
| URL | https://yourdomain.com/api/data |
| Method | GET |
| Expected status | 200 |
| Check interval | 2 minutes |
Create both monitors in Vigilmon under Monitors → New HTTP Monitor.
Step 4: Heartbeat for Background Jobs
If your backend runs any scheduled tasks (email queues, report generators, sync jobs), add a heartbeat monitor.
// Node.js example
import cron from "node-cron";
const HEARTBEAT_URL = process.env.VIGILMON_HEARTBEAT_URL ?? "";
async function pingHeartbeat() {
if (!HEARTBEAT_URL) return;
try {
await fetch(HEARTBEAT_URL, { signal: AbortSignal.timeout(5000) });
} catch {
// never crash the job due to monitoring failure
}
}
cron.schedule("0 * * * *", async () => {
try {
await runHourlyJob();
await pingHeartbeat();
} catch (err) {
console.error("Job failed:", err);
}
});
For Laravel, use the scheduler:
// app/Console/Kernel.php
protected function schedule(Schedule $schedule): void
{
$schedule->call(function () {
// your job logic
Http::get(config('services.vigilmon.heartbeat_url'));
})->hourly();
}
In Vigilmon:
- Monitors → New Heartbeat Monitor
- Set expected interval to 90 minutes
- Add the ping URL to your environment:
VIGILMON_HEARTBEAT_URL=https://vigilmon.online/api/heartbeat/xxxxxxxx
Step 5: Global Error Hook via Alpine Magic
Alpine's Alpine.magic() lets you register a global helper you can call anywhere. Pair it with a Vigilmon webhook to capture unhandled fetch errors from Alpine components.
<script>
document.addEventListener("alpine:init", () => {
Alpine.magic("reportError", () => (message, context = {}) => {
const webhookUrl = document.querySelector("meta[name='vigilmon-webhook']")
?.getAttribute("content");
if (!webhookUrl) return;
// Simple dedup via sessionStorage
const key = "err_" + message.slice(0, 40);
if (sessionStorage.getItem(key)) return;
sessionStorage.setItem(key, "1");
fetch(webhookUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
text: `Alpine.js error: ${message}`,
url: window.location.href,
context,
}),
}).catch(() => {});
});
});
</script>
<!-- In your layout, store the webhook URL in a meta tag (server-rendered) -->
<meta name="vigilmon-webhook" content="{{ config('services.vigilmon.webhook_url') }}">
Use $reportError in any Alpine component:
<div
x-data="{
async loadData() {
try {
const res = await fetch('/api/data');
if (!res.ok) throw new Error('HTTP ' + res.status);
return res.json();
} catch (err) {
$reportError(err.message, { component: 'data-loader' });
throw err;
}
}
}"
x-init="loadData()"
>
Step 6: Configure Alert Channels
- Alert Channels → Email
- Add your team's on-call email
- Attach to the server health monitor and API endpoint monitor
Slack Webhook
- Create a Slack Incoming Webhook for
#alerts - In Vigilmon, Alert Channels → Webhook
- Paste the Slack URL:
{
"text": "🚨 *{{ monitor.name }}* is DOWN\n{{ monitor.url }}\nStatus: {{ event.status }}\n<https://vigilmon.online|Open Vigilmon>"
}
Step 7: Test Everything
# 1. Verify health endpoint
curl -s https://yourdomain.com/health | jq .
# 2. Temporarily return 503 and confirm the alert fires
# 3. Use Vigilmon's "Test Alert" button to verify Slack/email delivery
# 4. Stop the cron job, wait 90+ minutes → heartbeat alert fires
What You Now Have
| Monitor | Catches | |---|---| | HTTP health check | Server crashes, DB failures, deploy errors | | API endpoint check | Key routes returning errors or going down | | Heartbeat | Background jobs stopping or crashing | | Error webhook | Runtime fetch errors in Alpine components |
Next Steps
- Add a Vigilmon status page and link it from your app footer
- Monitor separate API endpoints for each major Alpine component that fetches data
- Use Vigilmon's response time history to track latency on your busiest endpoints
Found this useful? Vigilmon is free to start — sign up here and have your first monitor live in under 5 minutes.