Polymer is Google's library for building web components and Progressive Web Apps (PWAs) on top of the Web Components standard. It pioneered the modern web component ecosystem and remains widely deployed in production PWAs. But even a perfectly crafted Polymer PWA fails silently when its service worker caches stale data, the backend API goes down, or a background sync job stops running. Vigilmon gives you the visibility layer you need: continuous HTTP monitoring, heartbeat checks, and instant alerts via email or Slack.
What You'll Build
- A health API endpoint for your Polymer PWA's backend
- Vigilmon HTTP monitors for your PWA shell and backend
- A heartbeat for background sync and scheduled tasks
- Alert channels (email and Slack)
- An uptime badge Polymer element
Prerequisites
- A Polymer 3.x project (or Lit-based migration)
- Node.js backend (Express or Firebase Functions) for the health endpoint
- A free Vigilmon account
Step 1: Add a Health Endpoint
Polymer renders in the browser; your health checks live in the backend serving your PWA's API:
// server.js (Express)
import express from "express";
const app = express();
app.get("/health", async (req, res) => {
const checks = {};
let degraded = false;
// Database / Firestore check
try {
// await db.collection("_health").doc("ping").get();
checks.database = "ok";
} catch (err) {
checks.database = `error: ${err.message}`;
degraded = true;
}
// Firebase Auth / Cloud check
try {
const resp = await fetch("https://www.googleapis.com/", {
signal: AbortSignal.timeout(3000),
});
checks.googleCloud = resp.status < 500 ? "ok" : `http_${resp.status}`;
} catch {
checks.googleCloud = "unreachable";
degraded = true;
}
res.status(degraded ? 503 : 200).json({
status: degraded ? "degraded" : "ok",
timestamp: new Date().toISOString(),
checks,
});
});
// Serve Polymer PWA static files
app.use(express.static("build/default"));
app.listen(process.env.PORT ?? 8080, () => {
console.log("Server running on port", process.env.PORT ?? 8080);
});
Test it:
curl http://localhost:8080/health | jq .
# {"status":"ok","timestamp":"2026-06-29T...","checks":{"database":"ok","googleCloud":"ok"}}
Step 2: Configure Vigilmon HTTP Monitors
Log in to Vigilmon → Monitors → New Monitor:
Monitor 1: Polymer PWA Shell
| Field | Value |
|---|---|
| URL | https://yourpwa.com/ |
| Method | GET |
| Expected status | 200 |
| Expected body | A stable string from your app shell HTML |
| Check interval | 1 minute |
Monitor 2: Health API
| Field | Value |
|---|---|
| URL | https://yourpwa.com/health |
| Method | GET |
| Expected status | 200 |
| Check interval | 1 minute |
Monitor 3: Service Worker Manifest (optional)
| Field | Value |
|---|---|
| URL | https://yourpwa.com/manifest.json |
| Method | GET |
| Expected status | 200 |
| Expected body | "name" |
| Check interval | 5 minutes |
Step 3: Heartbeat for Background Sync Tasks
PWAs rely on background sync APIs and server-side sync jobs. A Vigilmon heartbeat catches when these stop running:
npm install node-cron
// background-sync.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 {
// Never crash the sync job due to monitoring failure
}
}
// Example: push notification sync every 5 minutes
cron.schedule("*/5 * * * *", async () => {
try {
await processPushNotificationQueue();
await pingHeartbeat();
} catch (err) {
console.error("[sync] Push notification sync failed:", err);
// No ping → Vigilmon fires after the grace window
}
});
// Example: PWA data prefetch sync every hour
cron.schedule("0 * * * *", async () => {
try {
await prefetchAppShellData();
// Use a separate heartbeat URL for this job if needed
} catch (err) {
console.error("[sync] Prefetch failed:", err);
}
});
async function processPushNotificationQueue() {
// Your push notification processing logic
}
async function prefetchAppShellData() {
// Your app shell data prefetch 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 as a Polymer Element
Polymer 3.x uses ES module-based PolymerElement or you can use the lighter LitElement base. Here's a standalone uptime badge element:
// src/uptime-badge.js
import { PolymerElement, html } from "@polymer/polymer/polymer-element.js";
const BADGE_URL = "https://vigilmon.online/api/badge/your-monitor-id.svg";
const STATUS_URL = "https://vigilmon.online/status/your-monitor-slug";
class UptimeBadge extends PolymerElement {
static get template() {
return html`
<style>
:host {
display: inline-flex;
align-items: center;
}
img {
opacity: 0;
transition: opacity 0.2s;
}
img.loaded {
opacity: 1;
}
</style>
<a
href="${STATUS_URL}"
target="_blank"
rel="noopener noreferrer"
aria-label="Service uptime status"
>
<img
id="badge"
src="${BADGE_URL}"
alt="Uptime"
width="120"
height="20"
on-load="_onLoad"
/>
</a>
`;
}
_onLoad() {
this.$.badge.classList.add("loaded");
}
}
customElements.define("uptime-badge", UptimeBadge);
Use it in your Polymer app shell:
<!-- src/my-app.html or my-app.js template -->
<link rel="import" href="uptime-badge.html" />
<app-drawer-layout>
<app-header-layout>
<slot></slot>
</app-header-layout>
<footer>
<uptime-badge></uptime-badge>
</footer>
</app-drawer-layout>
Step 5: Set Up Alert Channels
In Vigilmon's Alert Channels settings:
- Alert Channels → Email → add your on-call email
- Attach to all 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>"
}
PagerDuty (for critical PWA incidents)
If your PWA has SLA commitments, hook Vigilmon into PagerDuty:
- Create a PagerDuty Events API v2 integration
- Alert Channels → Webhook in Vigilmon → paste the PagerDuty endpoint URL
- Set alerting threshold to 2 consecutive failures before paging on-call
Step 6: Verify Everything Works
# 1. Health endpoint
curl -s https://yourpwa.com/health | jq .
# 2. Break the DB connection string → confirm health returns 503
# Expected: Vigilmon alerts within 2 minutes
# 3. Stop the push notification sync cron → let heartbeat window expire
# Expected: heartbeat alert fires after the grace window
# 4. Vigilmon → "Test Alert" → confirm delivery to Slack/email
Also verify your service worker isn't masking outages by serving stale cached responses:
// In your service worker (sw.js)
// Network-first strategy for /health so Vigilmon always hits the live endpoint
self.addEventListener("fetch", (event) => {
if (event.request.url.endsWith("/health")) {
event.respondWith(fetch(event.request)); // Always go to network for health checks
return;
}
// ... your normal caching strategies
});
Summary
| Monitor | What It Catches | |---|---| | HTTP PWA shell check | CDN failures, broken app shell deploys | | HTTP health API | Database, Firestore, and Google Cloud failures | | HTTP manifest check | Broken PWA install experience | | Heartbeat | Push notification sync crashes, prefetch failures |
Next Steps
- Create separate monitors for staging, canary, and production channels
- Use Vigilmon's response time graphs to detect backend latency regressions affecting Time to Interactive
- Correlate Vigilmon downtime events with Lighthouse PWA scores to catch degraded offline experiences
- Configure multi-channel alerting: email for low-severity, Slack + PagerDuty for critical
Ready to get started? Sign up for Vigilmon free — have your first monitor running in under 5 minutes.