tutorial

How to Monitor Solid Start with Vigilmon

Add production-grade uptime monitoring to a Solid Start app — health endpoints, heartbeat checks, and instant alerts when your SolidJS meta-framework app goes down.

Solid Start is the official meta-framework for SolidJS — it brings file-based routing, server functions, streaming SSR, and edge deployment to the fastest reactive UI runtime available. But fine-grained reactivity and server rendering both fail silently when your database is gone, your deployment is broken, or your scheduled jobs have been crashing for hours. Vigilmon provides 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 route in your Solid Start app
  • Vigilmon HTTP monitors for the SSR app and its health endpoint
  • A heartbeat for scheduled background tasks
  • Alert channels (email and Slack)
  • An uptime badge in your Solid Start UI using SolidJS reactivity

Prerequisites

  • A Solid Start project (npm create solid@latest)
  • A deployment target (Node.js server, Vercel, Netlify, or Cloudflare)
  • A free Vigilmon account

Step 1: Add a Health API Route

Solid Start supports API routes alongside page routes in the src/routes/ directory. Create a health endpoint:

// src/routes/api/health.ts
import { json } from "@solidjs/router";
import type { APIEvent } from "@solidjs/start/server";

export async function GET(_event: APIEvent) {
  const checks: Record<string, string> = {};
  let degraded = false;

  // Database check
  try {
    // Replace with your actual DB check, e.g. Prisma, Drizzle, libSQL
    // await db.execute("SELECT 1");
    checks.database = "ok";
  } catch (err) {
    checks.database = `error: ${(err as Error).message}`;
    degraded = true;
  }

  // Third-party API reachability
  try {
    const res = await fetch("https://api.stripe.com/v1/", {
      signal: AbortSignal.timeout(3000),
    });
    checks.stripe = res.status < 500 ? "ok" : `http_${res.status}`;
  } catch {
    checks.stripe = "unreachable";
    degraded = true;
  }

  // Cache / KV check (optional)
  try {
    // await redis.ping();
    checks.cache = "ok";
  } catch (err) {
    checks.cache = `error: ${(err as Error).message}`;
    degraded = true;
  }

  return json(
    {
      status: degraded ? "degraded" : "ok",
      timestamp: new Date().toISOString(),
      checks,
    },
    { status: degraded ? 503 : 200 }
  );
}

Test it:

curl http://localhost:3000/api/health | jq .
# {"status":"ok","timestamp":"2026-07-03T...","checks":{"database":"ok","stripe":"ok","cache":"ok"}}

Step 2: Configure Vigilmon HTTP Monitors

Log in to VigilmonMonitors → New Monitor:

Monitor 1: Solid Start App (SSR)

| Field | Value | |---|---| | URL | https://yourapp.com/ | | Method | GET | | Expected status | 200 | | Expected body | A stable string from your rendered HTML (e.g. your page title) | | Check interval | 1 minute |

Monitor 2: Health API Route

| Field | Value | |---|---| | URL | https://yourapp.com/api/health | | Method | GET | | Expected status | 200 | | Check interval | 1 minute |

Separating these two monitors means a Node.js crash and a database failure each trigger distinct alerts — accurate attribution speeds up incident response.


Step 3: Heartbeat for Background Tasks

Solid Start's server functions handle real-time work, but periodic jobs — email digests, report generation, data syncs — are typically run separately. Use a Vigilmon heartbeat to catch silent failures:

// src/lib/scheduler.ts
import cron from "node-cron";

const HEARTBEAT_URL = process.env.VIGILMON_HEARTBEAT_URL ?? "";

async function pingHeartbeat(): Promise<void> {
  if (!HEARTBEAT_URL) return;
  try {
    await fetch(HEARTBEAT_URL, { signal: AbortSignal.timeout(5000) });
  } catch {
    // Monitoring must never crash the job
  }
}

cron.schedule("*/10 * * * *", async () => {
  try {
    await processNotificationQueue();
    await pingHeartbeat(); // Only ping on success
  } catch (err) {
    console.error("[scheduler] Queue processing failed:", err);
    // No heartbeat ping → Vigilmon alerts after the grace window
  }
});

async function processNotificationQueue(): Promise<void> {
  // Your actual queue logic here
}

Start the scheduler alongside your Solid Start server:

// src/entry-server.tsx (or a separate process in larger apps)
import "./lib/scheduler";
import { createHandler, renderAsync } from "@solidjs/start/server";
// ... rest of your entry-server setup

Set up the heartbeat in Vigilmon:

  1. Monitors → New Heartbeat Monitor
  2. Set Expected interval to 25 minutes (2.5× the cron interval for a generous grace window)
  3. Add the ping URL to your .env:
VIGILMON_HEARTBEAT_URL=https://vigilmon.online/api/heartbeat/xxxxxxxx

Step 4: Display an Uptime Badge in Your Solid Start App

SolidJS's fine-grained reactivity — createSignal, createResource — makes it trivial to load external status data without unnecessary re-renders:

// src/components/StatusBadge.tsx
import { createResource, Show } from "solid-js";

const BADGE_URL = "https://vigilmon.online/api/badge/your-monitor-id.svg";
const STATUS_PAGE = "https://vigilmon.online/status/your-monitor-slug";

function StatusBadge() {
  // Use createResource to fetch live status without blocking SSR
  const [status] = createResource(async () => {
    const res = await fetch(`${STATUS_PAGE}.json`).catch(() => null);
    return res?.ok ? res.json() : null;
  });

  return (
    <a
      href={STATUS_PAGE}
      target="_blank"
      rel="noopener noreferrer"
      aria-label="Service uptime status"
      class="inline-flex items-center gap-2"
    >
      <img src={BADGE_URL} alt="Uptime" width={120} height={20} />
      <Show when={status()?.status}>
        <span class={`status-indicator status-${status()!.status}`}>
          {status()!.status}
        </span>
      </Show>
    </a>
  );
}

export default StatusBadge;

Add it to your root layout:

// src/routes/(layout).tsx
import { Outlet } from "@solidjs/router";
import StatusBadge from "~/components/StatusBadge";

export default function Layout() {
  return (
    <div>
      <main>
        <Outlet />
      </main>
      <footer class="site-footer">
        <StatusBadge />
      </footer>
    </div>
  );
}

Step 5: Set Up Alert Channels

In Vigilmon's Alert Channels settings:

Email

  1. Alert Channels → Email → add your on-call address
  2. Attach the channel to both HTTP monitors and the heartbeat

Slack

  1. Create a Slack Incoming Webhook for #alerts
  2. Alert Channels → Webhook → paste the URL
  3. Use this payload template:
{
  "text": "🚨 *{{ monitor.name }}* is DOWN\n{{ monitor.url }}\nStatus: {{ event.status }}\n<https://vigilmon.online|Open Vigilmon>"
}

Step 6: Verify the Setup

# 1. Confirm health route
curl -s https://yourapp.com/api/health | jq .

# 2. Force a DB failure — disconnect the database, restart the server
# Expected: /api/health returns 503; Vigilmon alerts within 2 minutes

# 3. Deploy a broken build that crashes the SSR server
# Expected: Monitor 1 alerts; Monitor 2 may also alert if health route is affected

# 4. Stop the scheduler without pinging the heartbeat
# Expected: alert fires after the grace window

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

Summary

| Monitor | What It Catches | |---|---| | HTTP SSR check | Node.js crashes, broken deployments, routing failures | | HTTP health API | Database, cache, and third-party API failures | | Heartbeat | Silent scheduler crashes, queue processing failures |


Next Steps

  • Create separate Vigilmon monitors for staging vs. production environments
  • Use response time graphs to detect SSR latency regressions correlated with data loading bottlenecks
  • Monitor individual server function endpoints that are critical to your business logic
  • Configure multi-channel alerting: email for warnings, Slack or PagerDuty for critical failures

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 →