Xata is a serverless database platform that combines a relational-style data store with built-in full-text search and a branch-based workflow for schema changes. It eliminates the operational overhead of running PostgreSQL clusters while adding developer-friendly features like zero-downtime migrations and type-safe client libraries. But serverless does not mean invisible — Xata API limits, branch misconfiguration, search index lag, and the occasional regional degradation can all cause your application to return errors or stale data without obvious symptoms.
Vigilmon gives you external visibility into Xata availability through HTTP probe monitoring and heartbeat monitoring for Xata-dependent background processes. This tutorial covers both.
Why Xata Monitoring Matters
Xata's status page shows aggregate service health. It cannot tell you:
- Whether your specific database branch is reachable and accepting queries
- Whether API key rotation or permission changes have silently broken your app's access
- Whether full-text search indexes are lagging behind writes and returning stale results
- Whether your app has hit Xata's request rate limits and is receiving throttled responses
- Whether your Xata-dependent background workers (sync jobs, data importers) have silently stopped
These failures appear as application-level errors rather than database outages, making them difficult to diagnose without external monitoring. Vigilmon tests the actual path your application uses.
Step 1: Build a Xata Health Endpoint
Create a thin health endpoint in your backend that queries Xata and returns HTTP 200 or 503. This tests your API key, the target branch, and basic query execution together.
Node.js Example (Express)
// xata-health.js
const express = require('express');
const { getXataClient } = require('./src/xata'); // generated Xata client
const app = express();
const xata = getXataClient();
app.get('/health/xata', async (req, res) => {
const start = Date.now();
try {
// Perform a lightweight query — count a small table or fetch one record
const result = await xata.db._healthcheck.getFirst();
// If _healthcheck table doesn't exist, a simple branch query works too
// const branches = await xata.branches.list();
return res.status(200).json({
status: 'ok',
latency_ms: Date.now() - start,
});
} catch (err) {
return res.status(503).json({
status: 'down',
error: err.message,
});
}
});
// Separate endpoint to test full-text search availability
app.get('/health/xata/search', async (req, res) => {
const start = Date.now();
try {
await xata.search.all('_healthcheck', { tables: [{ table: '_healthcheck' }] });
return res.status(200).json({ status: 'ok', latency_ms: Date.now() - start });
} catch (err) {
return res.status(503).json({ status: 'down', error: err.message });
}
});
app.listen(3001);
Python Example (FastAPI)
# xata_health.py
import os, time
from xata.client import XataClient
from fastapi import FastAPI
from fastapi.responses import JSONResponse
app = FastAPI()
client = XataClient(
api_key=os.environ['XATA_API_KEY'],
db_url=os.environ['XATA_DATABASE_URL'],
)
@app.get('/health/xata')
def xata_health():
start = int(time.time() * 1000)
try:
# Lightweight query to verify connectivity and auth
result = client.data().query('_healthcheck', payload={'page': {'size': 1}})
return {'status': 'ok', 'latency_ms': int(time.time() * 1000) - start}
except Exception as e:
return JSONResponse(status_code=503, content={
'status': 'down',
'error': str(e),
})
@app.get('/health/xata/search')
def xata_search_health():
start = int(time.time() * 1000)
try:
client.data().search_branch(
payload={'query': '_healthcheck', 'tables': [{'table': '_healthcheck'}]}
)
return {'status': 'ok', 'latency_ms': int(time.time() * 1000) - start}
except Exception as e:
return JSONResponse(status_code=503, content={
'status': 'down',
'error': str(e),
})
Create a minimal _healthcheck table in Xata with a single text column. This gives the health endpoint a safe target that won't affect production data.
Verify the endpoint manually:
curl -i https://your-app.example.com/health/xata
# HTTP/1.1 200 OK
# {"status":"ok","latency_ms":35}
Step 2: Configure Vigilmon HTTP Monitors
- Log in to vigilmon.online and go to Monitors → New Monitor
- Choose HTTP / HTTPS
- Set the URL to your Xata health endpoint:
https://your-app.example.com/health/xata - Set the check interval to 1 minute
- Under Expected response, configure:
- Status code:
200 - Response body contains:
"status":"ok" - Response time threshold:
2000ms
- Status code:
- Under Alert channels, assign your Slack or email channel
- Save the monitor
Repeat for the search health endpoint (/health/xata/search) as a separate monitor with a slightly more lenient threshold — search index queries are inherently slower than record lookups.
Vigilmon probes from multiple geographic regions and requires multi-region consensus before opening an incident. A single transient probe failure will not page you.
What This Catches
| Failure | Xata Status Page | Vigilmon | |---|---|---| | Xata regional outage | ✓ | ✓ | | API key expiry or permission change | ✗ | ✓ | | Wrong branch in environment config | ✗ | ✓ | | Search index unavailability | ✗ | ✓ | | Rate limit throttling | ✗ | ✓ |
Step 3: Monitor the Xata REST API Directly
You can probe the Xata REST API directly from Vigilmon without a wrapper endpoint. The Xata REST API accepts API key authentication and returns structured JSON.
Create a Vigilmon monitor with these settings:
- URL:
https://{workspace}.{region}.xata.sh/db/{database}:{branch}/tables - Method: GET
- Headers:
Authorization: Bearer YOUR_XATA_API_KEY - Expected status code:
200 - Response body contains:
"tables"
Replace {workspace}, {region}, {database}, and {branch} with your Xata database details. This validates that your API key is valid and the branch is accessible without deploying any wrapper code.
Step 4: Heartbeat Monitoring for Xata Background Workers
If you run background workers that sync data into Xata (ETL pipelines, event processors, data importers), those workers will stop silently when Xata is unavailable or when API keys expire. Data falls behind, and the discrepancy may not surface until a user queries stale records.
Vigilmon heartbeat monitors catch this: your worker pings Vigilmon after each successful processing cycle. If the ping stops, Vigilmon opens an incident.
Set Up the Heartbeat Monitor
- In Vigilmon, go to Monitors → New Monitor → Heartbeat
- Name it:
xata-data-sync-worker - Set the expected interval: 10 minutes (adjust to your sync frequency)
- Set the grace period: 20 minutes
- Save — copy the heartbeat URL
Wire Into Your Worker
const axios = require('axios');
const { getXataClient } = require('./src/xata');
const xata = getXataClient();
async function syncEvents() {
const events = await fetchPendingEvents();
for (const event of events) {
await xata.db.events.create({
event_id: event.id,
type: event.type,
payload: JSON.stringify(event.data),
processed_at: new Date().toISOString(),
});
await markEventProcessed(event.id);
}
// Ping Vigilmon after successful sync cycle
await axios.get(process.env.VIGILMON_HEARTBEAT_URL).catch(() => {});
}
// Run every 10 minutes
setInterval(syncEvents, 10 * 60 * 1000);
syncEvents();
Step 5: Branch-Aware Monitoring for Staging Environments
Xata's branch-based workflow means your staging environment points to a different branch than production. Monitor each branch independently to catch configuration drift:
- Production branch monitor → PagerDuty + Slack (P1)
- Staging branch monitor → Slack only (P2 — important for catching pre-deploy issues)
Use environment variables to parameterize your health endpoint so it reflects the current branch:
const branchName = process.env.XATA_BRANCH || 'main';
const databaseUrl = `https://${workspace}.${region}.xata.sh/db/${database}:${branchName}`;
This ensures that each deployment environment's health endpoint tests the correct branch, and Vigilmon monitors validate the actual configuration in use.
Step 6: Alerting Best Practices for Xata
Xata outages fall into two categories: data unavailability (query failures, auth errors) and search unavailability (index lag, search API errors). Route alerts accordingly:
- Xata data health monitor → Slack + PagerDuty (P1 — data reads/writes failing)
- Xata search health monitor → Slack + email (P2 — search degraded but data available)
- Data sync heartbeat → Slack + email (P2 — background sync stalled)
- Response time alert at 1500ms → Slack only (early warning for API pressure)
Set up a Vigilmon Status Page that groups all Xata monitors together. When an incident opens, your team sees the full picture — whether it's a data issue, a search issue, or a background worker failure.
Summary
Xata's serverless model makes it easy to build on, but the same abstractions that simplify operations can make failures harder to detect. Vigilmon gives you external visibility before users notice:
| Monitor Type | What It Covers |
|---|---|
| HTTP monitor on /health/xata | Query availability, auth, branch config |
| HTTP monitor on /health/xata/search | Full-text search index availability |
| HTTP monitor on Xata REST API | Direct API key and branch validation |
| Heartbeat monitor | Background data sync worker liveness |
Get started free at vigilmon.online — your first Xata monitor is running in under two minutes.