tutorial

Monitoring Your Stencil App with Vigilmon: Health Checks, Heartbeats & Uptime Alerts

Add production-grade monitoring to a Stencil web components application — a health API, background job heartbeats, and instant alerts when your custom elements or backend go down.

Stencil compiles your components into standards-based custom elements that run natively in the browser — no framework runtime, lazy-loaded chunks, and full TypeScript support out of the box. But custom elements shipping fast to users still doesn't protect you from server outages, broken APIs, or silently failing background jobs. Vigilmon gives you the production observability your Stencil app needs: continuous HTTP monitoring, heartbeat checks, and instant alerts routed to email or Slack.

What You'll Build

  • A health API endpoint for your Stencil application backend
  • Vigilmon HTTP monitors for your Stencil component library host and backend API
  • A heartbeat for scheduled background tasks
  • Alert channels (email and Slack)
  • An uptime badge as a Stencil web component

Prerequisites

  • A Stencil project (created with npm init stencil)
  • Node.js backend for the health endpoint
  • A free Vigilmon account

Step 1: Add a Health Endpoint

Stencil components are client-side custom elements; your backend exposes data APIs and serves the component bundles. Add a /health route:

// 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 API check
  try {
    const response = await fetch("https://api.example.com/ping", {
      signal: AbortSignal.timeout(3000),
    });
    checks.external_api = response.ok ? "ok" : `http_${response.status}`;
  } catch (err) {
    checks.external_api = "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","external_api":"ok"}}

Step 2: Configure Vigilmon HTTP Monitors

Log in to VigilmonMonitors → New Monitor:

Monitor 1: Stencil App Host (component entry page)

| Field | Value | |---|---| | URL | https://yourapp.com/ | | Method | GET | | Expected status | 200 | | Expected body | A stable tag from your HTML (e.g. your root custom element <my-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 catch different failure classes: the CDN/static layer serving your custom element bundles, and the backend data layer your components depend on.


Step 3: Heartbeat for Background Tasks

Stencil apps often consume data from scheduled backend pipelines — content indexing, cache hydration, push notification batches. Silent failures there break your components' data without any visible error.

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("*/5 * * * *", async () => {
  try {
    await hydrateComponentData();
    await pingHeartbeat();
  } catch (err) {
    console.error("[scheduler] Data hydration failed:", err);
    // Vigilmon alerts after the heartbeat window expires with no ping
  }
});

async function hydrateComponentData() {
  // Pre-fetch and cache data for Stencil components
}

Set up the heartbeat in Vigilmon:

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

Step 4: Build an Uptime Badge as a Stencil Component

Stencil's component model is ideal for an encapsulated status badge you can drop into any host:

// src/components/vigilmon-badge/vigilmon-badge.tsx
import { Component, Prop, State, h } from "@stencil/core";

@Component({
  tag: "vigilmon-badge",
  shadow: true,
  styles: `
    :host { display: inline-flex; align-items: center; }
    img { transition: opacity 0.2s; }
  `,
})
export class VigilmonBadge {
  @Prop() monitorId!: string;
  @Prop() monitorSlug!: string;
  @State() loaded = false;

  private get badgeUrl() {
    return `https://vigilmon.online/api/badge/${this.monitorId}.svg`;
  }

  private get statusUrl() {
    return `https://vigilmon.online/status/${this.monitorSlug}`;
  }

  render() {
    return (
      <a
        href={this.statusUrl}
        target="_blank"
        rel="noopener noreferrer"
        aria-label="Service uptime status"
      >
        <img
          src={this.badgeUrl}
          alt="Uptime"
          width={120}
          height={20}
          style={{ opacity: this.loaded ? "1" : "0" }}
          onLoad={() => (this.loaded = true)}
        />
      </a>
    );
  }
}

Use the component anywhere in your HTML:

<!-- index.html -->
<script type="module" src="/build/myapp.esm.js"></script>
<footer>
  <vigilmon-badge
    monitor-id="your-monitor-id"
    monitor-slug="your-monitor-slug"
  ></vigilmon-badge>
</footer>

Because the badge is a true custom element, it works in any framework or plain HTML page without modification.


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
curl -s https://yourapp.com/health | jq .

# 2. Break the DB connection string → confirm health returns 503
# Expected: Vigilmon alerts within 2 minutes

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

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

Summary

| Monitor | What It Catches | |---|---| | HTTP app host check | CDN failures, broken static bundle deploys | | HTTP health API | Database, cache, and third-party API failures | | Heartbeat | Scheduler crashes, silent data hydration failures |


Next Steps

  • Create separate monitors for staging vs. production
  • Use Vigilmon's response time graphs to detect API latency spikes affecting your Stencil lazy-loaded components
  • Configure multi-channel alerting: email for low-severity, Slack for critical
  • Publish your <vigilmon-badge> component to npm so other teams can reuse it across host applications

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 →