WunderGraph is an API federation and composition platform that sits in front of multiple data sources — GraphQL APIs, REST endpoints, databases, and gRPC services — and exposes a unified, type-safe API surface. It handles schema stitching, request caching, authentication, and operation-level access control, so your clients consume a single coherent API regardless of how many upstream services back it. What WunderGraph cannot provide on its own is an independent, external health check: a synthetic probe that measures whether your API gateway is reachable from outside your infrastructure and that specific operations return correct data. Vigilmon provides that. It sits entirely outside your network, probes your WunderGraph gateway from multiple geographic regions, and alerts you the moment the gateway or a critical operation becomes unavailable.
This tutorial covers adding external uptime monitoring to a self-hosted WunderGraph gateway.
What You'll Build
- A
/healthroute in your WunderGraph server configuration - A Vigilmon HTTP monitor with operation-level response assertions
- A data source connectivity check exposed through the health endpoint
- A heartbeat monitor for WunderGraph background operations
- Alert channels that notify your team when the gateway or a critical data source goes down
Prerequisites
- Node.js 18+ with a WunderGraph project initialized (
npx create-wundergraph-app) - A deployed WunderGraph gateway accessible via HTTPS
- A free account at vigilmon.online
Step 1: Add a Health Endpoint to Your WunderGraph Server
WunderGraph's server configuration uses configureWunderGraphServer with custom hooks. Add a health route using the customGqlServerMount or, for WunderGraph's newer server SDK, a custom Express middleware endpoint.
WunderGraph Server SDK health route
// wundergraph.server.ts
import { configureWunderGraphServer } from '@wundergraph/sdk/server';
export default configureWunderGraphServer(() => ({
hooks: {
// Operation hooks go here
},
routerConfig: (config) => {
// Add a custom health route
config.router.get('/health', async (req, res) => {
const checks: Record<string, string> = {};
let ok = true;
// Check the WunderGraph internal engine status
try {
const engineResp = await fetch('http://localhost:9991/health', {
signal: AbortSignal.timeout(3000),
});
checks.engine = engineResp.ok ? 'ok' : `http_${engineResp.status}`;
if (!engineResp.ok) ok = false;
} catch (err) {
checks.engine = `error: ${(err as Error).message}`;
ok = false;
}
// Check a critical data source (e.g., your primary GraphQL API)
const primaryApiUrl = process.env.PRIMARY_GRAPHQL_API_URL;
if (primaryApiUrl) {
try {
const resp = await fetch(primaryApiUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: '{ __typename }' }),
signal: AbortSignal.timeout(5000),
});
checks.primary_api = resp.ok ? 'ok' : `http_${resp.status}`;
if (!resp.ok) ok = false;
} catch (err) {
checks.primary_api = `error: ${(err as Error).message}`;
ok = false;
}
}
// Check a REST data source
const restApiUrl = process.env.REST_API_URL;
if (restApiUrl) {
try {
const resp = await fetch(`${restApiUrl}/ping`, {
signal: AbortSignal.timeout(5000),
});
checks.rest_api = resp.ok ? 'ok' : `http_${resp.status}`;
if (!resp.ok) ok = false;
} catch (err) {
checks.rest_api = `error: ${(err as Error).message}`;
ok = false;
}
}
res.status(ok ? 200 : 503).json({
status: ok ? 'ok' : 'degraded',
timestamp: new Date().toISOString(),
checks,
});
});
return config;
},
}));
Test locally:
wundergraph up
curl http://localhost:9991/health
curl http://localhost:9992/health # Your custom server route
Expected response:
{
"status": "ok",
"timestamp": "2026-07-06T10:00:00.000Z",
"checks": {
"engine": "ok",
"primary_api": "ok",
"rest_api": "ok"
}
}
Step 2: Operation-Level Probe Endpoint
For GraphQL gateways, a process-level health check is not enough — you also want to verify that a critical operation resolves successfully end-to-end through the federation layer. Add a lightweight probe operation:
// .wundergraph/operations/HealthProbe.graphql
query HealthProbe {
# Replace with a fast, low-cost operation from your actual schema
__typename
}
WunderGraph exposes this as a type-safe JSON endpoint at GET /operations/HealthProbe. Add a second Vigilmon monitor that targets this URL and asserts on the response body:
GET https://your-gateway.example.com/operations/HealthProbe
Expected response:
{
"data": {
"__typename": "Query"
}
}
Configure the Vigilmon monitor to assert: keyword "__typename" present in the response body.
For a more meaningful probe, use a real lightweight read operation from your schema:
// .wundergraph/operations/HealthProbe.graphql
query HealthProbe {
# A real, fast read from one of your data sources
users(first: 1) {
__typename
}
}
This catches data source federation failures that a process-level health check would miss.
Step 3: Configure Vigilmon HTTP Monitors
Gateway health monitor
- Log in to Vigilmon → Add Monitor → HTTP.
- URL:
https://your-gateway.example.com/health - Check interval: 60 seconds.
- Response timeout: 10 seconds.
- Expected status code:
200. - Keyword assertion:
"status":"ok". - Click Save.
Operation probe monitor
- Add Monitor → HTTP.
- URL:
https://your-gateway.example.com/operations/HealthProbe - Check interval: 60 seconds.
- Response timeout: 15 seconds — GraphQL federation adds latency.
- Expected status code:
200. - Keyword assertion:
"data"— confirms the operation resolved without errors. - Click Save.
Group both monitors under a Monitor group named WunderGraph Gateway.
Monitoring per data source environment
If your WunderGraph gateway serves multiple environments, create monitors for each:
| Environment | URL | Monitor type |
|---|---|---|
| Production gateway health | https://gateway.example.com/health | HTTP, 60 s |
| Production operation probe | https://gateway.example.com/operations/HealthProbe | HTTP, 60 s |
| Staging gateway health | https://gateway-staging.example.com/health | HTTP, 120 s |
Step 4: Heartbeat Monitor for WunderGraph Background Jobs
WunderGraph supports background polling for data source subscriptions, cache warming, and scheduled invalidation. Use Vigilmon heartbeats to detect when these background processes silently stop.
// scripts/cache-warm.ts
import fetch from 'node-fetch';
async function warmCache(): Promise<void> {
const gatewayUrl = process.env.WUNDERGRAPH_URL ?? 'http://localhost:9991';
// Warm the cache for the most-accessed operations
const operations = ['TopProducts', 'FeaturedUsers', 'RecentPosts'];
for (const op of operations) {
try {
const resp = await fetch(`${gatewayUrl}/operations/${op}`, {
headers: { 'X-WG-Cache': 'warm' },
signal: AbortSignal.timeout(10000),
});
if (!resp.ok) {
console.error(`Cache warm failed for ${op}: HTTP ${resp.status}`);
return; // Do not ping heartbeat — alert should fire
}
console.log(`Cache warmed: ${op}`);
} catch (err) {
console.error(`Cache warm error for ${op}:`, err);
return;
}
}
// All operations warmed — ping heartbeat
const heartbeatUrl = process.env.VIGILMON_HEARTBEAT_URL;
if (heartbeatUrl) {
await fetch(heartbeatUrl, { method: 'POST', signal: AbortSignal.timeout(5000) });
console.log('Heartbeat sent');
}
}
warmCache().catch(console.error);
Scheduled via cron
# Crontab — run cache warming every 10 minutes
*/10 * * * * node /app/scripts/cache-warm.js >> /var/log/cache-warm.log 2>&1
In Vigilmon, create a Heartbeat monitor with an expected interval of 15 minutes (5 minutes longer than the cron schedule). If the cache-warming job misses its window, Vigilmon alerts.
Step 5: Webhook Receiver for Vigilmon Events
Add a WunderGraph server route to receive Vigilmon DOWN/UP notifications and trigger internal recovery actions:
// In wundergraph.server.ts routerConfig:
config.router.post('/webhook/vigilmon', express.json(), async (req, res) => {
const payload = req.body as {
monitor_name: string;
status: 'down' | 'up';
url: string;
response_code: number;
checked_at: string;
};
if (payload.status === 'down') {
console.error('[VIGILMON] Gateway DOWN', payload);
// Trigger cache invalidation on recovery to avoid stale data
const slackUrl = process.env.SLACK_WEBHOOK_URL;
if (slackUrl) {
await fetch(slackUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
text: `*GATEWAY DOWN*: ${payload.monitor_name} — ${payload.url}`,
}),
});
}
} else {
console.info('[VIGILMON] Gateway UP', payload.monitor_name);
// Trigger cache revalidation after recovery
await revalidateCache();
}
res.json({ received: true });
});
async function revalidateCache(): Promise<void> {
// Trigger cache invalidation so stale responses don't serve post-recovery
console.info('Triggering cache revalidation after recovery');
}
In Vigilmon → Alerts → Add channel → Webhook, point to https://your-gateway.example.com/webhook/vigilmon.
Step 6: Alert Routing Strategy
WunderGraph's internal telemetry covers data source request traces, operation latencies, and cache hit rates. Vigilmon covers external availability — the gateway is unreachable, a critical operation times out, or a background job misses its heartbeat.
| Failure mode | Source | Route to |
|---|---|---|
| Data source query error spike | WunderGraph traces | Engineering team + ticket |
| Cache hit rate drop | WunderGraph metrics | Performance team |
| Gateway endpoint unreachable | Vigilmon | PagerDuty + Slack #prod-alerts |
| Operation probe failing | Vigilmon | On-call rotation |
| Background job heartbeat missed | Vigilmon | Slack #infra-ops-critical |
| TLS certificate expiring | Vigilmon | DevOps team |
Step 7: Public Status Page
API gateways serve external developers and partners. Give them real-time availability visibility through Vigilmon.
- In Vigilmon → Status Pages → Create.
- Add the gateway health monitor, the operation probe monitor, and any critical heartbeat monitors.
- Configure a custom domain (
status.api.example.com) or use the Vigilmon-provided subdomain. - Add a status badge to your API developer documentation:
<img src="https://vigilmon.online/badge/<monitor-id>.svg" alt="API Gateway Status" />
What Vigilmon Adds to a WunderGraph Gateway
| Capability | WunderGraph | Vigilmon | |---|---|---| | GraphQL schema federation | Yes | No | | Operation-level access control | Yes | No | | Request caching and revalidation | Yes | No | | Data source request traces | Yes | No | | Per-operation latency metrics | Yes | No | | External synthetic HTTP probing | No | Yes | | DNS resolution failure detection | No | Yes | | TLS/SSL certificate expiry alerts | No | Yes | | Scheduled job heartbeat monitoring | No | Yes | | Multi-region uptime probing | No | Yes | | Public API status page | No | Yes | | Independent of internal metric pipeline | No | Yes |
WunderGraph unifies your data sources behind a type-safe API layer and handles the federation complexity that would otherwise require dozens of resolver-level integrations. Vigilmon monitors the resulting unified API from the outside — the same vantage point your API consumers have. Together, they cover the full reliability picture: from data source health inside your infrastructure to gateway availability experienced by the developers and applications that depend on it.
Start monitoring your WunderGraph gateway externally — register free at vigilmon.online.