Real-Time Subscriptions
Excalibase GraphQL exposes real-time table changes as GraphQL subscriptions, powered by excalibase-watcher and NATS JetStream.
Works with PostgreSQL and MySQL. The watcher captures changes via logical replication (PostgreSQL) or binlog (MySQL) and publishes them to NATS.
How it works
Database change (INSERT / UPDATE / DELETE)
↓
WAL (PostgreSQL) or Binlog (MySQL)
↓
excalibase-watcher (CDC server)
↓
NATS JetStream (cdc.{schema}.{table})
↓
excalibase-graphql (NatsCDCService)
↓
GraphQL subscription (WebSocket)
↓
Client
The watcher owns the replication slot / binlog connection. Excalibase-graphql is a pure NATS consumer — this means you can scale horizontally without duplicate CDC events.
DDL changes (CREATE TABLE, ALTER TABLE, etc.) are also captured and automatically invalidate the GraphQL schema cache — no restart needed.
Setup
1. Enable logical replication (PostgreSQL)
-- postgresql.conf
wal_level = logical
max_replication_slots = 5
max_wal_senders = 5
The provided docker-compose.yml already has this configured, along with NATS and excalibase-watcher.
2. Configure NATS connection
app:
nats:
enabled: true
url: nats://localhost:4222
stream-name: CDC
subject-prefix: cdc
3. Subscribe to changes
subscription {
customerChanges {
operation # INSERT | UPDATE | DELETE | ERROR
table
timestamp
data {
customer_id
first_name
last_name
email
new { # populated on UPDATE
customer_id
first_name
last_name
email
}
}
error
}
}
Operation types
INSERT — full row data
{
"operation": "INSERT",
"data": {
"customer_id": 13,
"first_name": "John",
"last_name": "Doe",
"email": "john@example.com"
}
}
UPDATE — new values nested under new
{
"operation": "UPDATE",
"data": {
"new": {
"customer_id": 13,
"first_name": "Jane",
"last_name": "Doe",
"email": "jane@example.com"
}
}
}
DELETE — primary key only (REPLICA IDENTITY DEFAULT)
{
"operation": "DELETE",
"data": {
"customer_id": 13
}
}
REPLICA IDENTITY
Controls how much data appears in UPDATE/DELETE events:
-- Default: UPDATE/DELETE only include primary key
ALTER TABLE customer REPLICA IDENTITY DEFAULT;
-- Full: UPDATE/DELETE include all column values
ALTER TABLE customer REPLICA IDENTITY FULL;
Recommendation: use REPLICA IDENTITY FULL for tables where clients need the full row on DELETE or before/after values on UPDATE.
WebSocket endpoint
Subscriptions connect via WebSocket to:
ws://localhost:10000/graphql
Protocol: graphql-transport-ws. Most GraphQL clients (Apollo Client, urql, graphql-ws) support this automatically.
Horizontal scaling
Because excalibase-watcher owns the single replication slot and publishes to NATS, you can run multiple excalibase-graphql pods behind a load balancer. Each pod receives every event via NATS fan-out — no duplicate replication slots, no duplicate events.
Related
- excalibase-watcher documentation — CDC server setup and configuration
- excalibase-watcher on GitHub