PostgreSQL Realtime Monitor provides a live view of all database changes through a WebSocket-powered dashboard. Changes are captured using PostgreSQLโs logical replication feature and broadcast to all connected clients.
๐ Server running at http://localhost:3000๐ก WebSocket available at ws://localhost:3000/wsโ Realtime subscription ready (connected or reconnected)
-- Create multiple tablesCREATE TABLE users ( id SERIAL PRIMARY KEY, email TEXT UNIQUE NOT NULL, created_at TIMESTAMPTZ DEFAULT NOW());CREATE TABLE orders ( id SERIAL PRIMARY KEY, user_id INTEGER REFERENCES users(id), total DECIMAL(10,2), status TEXT, created_at TIMESTAMPTZ DEFAULT NOW());-- Insert dataINSERT INTO users (email) VALUES ('alice@example.com');INSERT INTO orders (user_id, total, status) VALUES (1, 99.99, 'pending');-- Update dataUPDATE orders SET status = 'completed' WHERE id = 1;
All changes appear in the dashboard, clearly labeled with their table name:
public.users - INSERT
public.orders - INSERT
public.orders - UPDATE
The table name includes the schema (e.g., public.users) to avoid ambiguity in databases with multiple schemas.
import asyncioimport websocketsimport jsonasync def monitor_changes(): uri = "ws://localhost:3000/ws" async with websockets.connect(uri) as websocket: print("Connected to PostgreSQL monitor") async for message in websocket: data = json.loads(message) if data['type'] == 'initial': print(f"Initial state: {len(data['data'])} changes") elif data['type'] == 'change': for change in data['data']: print(f"{change['operation']} on {change['table']}")asyncio.run(monitor_changes())
-- Create a trigger that updates a timestampCREATE OR REPLACE FUNCTION update_modified_column()RETURNS TRIGGER AS $$BEGIN NEW.modified_at = NOW(); RETURN NEW;END;$$ LANGUAGE plpgsql;CREATE TRIGGER update_todos_modtime BEFORE UPDATE ON todos FOR EACH ROW EXECUTE FUNCTION update_modified_column();-- Update a rowUPDATE todos SET title = 'Updated title' WHERE id = 1;
The monitor will show the UPDATE with the modified_at field automatically updated by the trigger.
BEGIN;INSERT INTO users (email) VALUES ('bob@example.com') RETURNING id;-- Assume id = 2INSERT INTO orders (user_id, total, status) VALUES (2, 149.99, 'pending');UPDATE users SET last_order_at = NOW() WHERE id = 2;COMMIT;
All three changes appear in the monitor in sequence, showing the complete transaction flow.
Changes are broadcast when the transaction commits. Rolled-back transactions do not generate change events.
In-memory storage: All changes are stored in memory and will be lost when the server restarts. This is intentional for monitoring use cases.
Memory Usage
Since changes accumulate in memory, the serverโs memory usage will grow over time. For long-running sessions monitoring high-traffic databases, consider:
Periodically restarting the server
Implementing a size limit on the allChanges array
Adding a โclearโ feature to reset the change list
Publication Scope
The default alltables publication captures changes from all tables. For fine-grained control:
-- Create a publication for specific tablesCREATE PUBLICATION specific_tables FOR TABLE users, orders;
Then update db.ts:
publications: 'specific_tables'
Replica Identity
For UPDATE and DELETE operations to include all column values, tables may need REPLICA IDENTITY FULL:
ALTER TABLE todos REPLICA IDENTITY FULL;
Without this, only the primary key values may be available for DELETE operations.