Realtime
pgStack's realtime layer is pg_reactive — a PostgreSQL C extension that turns any SELECT into a live query and pushes JSON deltas over LISTEN/NOTIFY. pgStack bundles the extension, runs the WebSocket fan-out proxy for you, and ties subscriptions into the platform's auth and Row-Level Security.
This page covers the pgStack integration. For the engine itself — standalone install, the full SQL subscription API, the wire protocol, GUCs, the security model, and the SDK — see the dedicated pg_reactive documentation.
How it fits together
A row changes → pg_reactive re-runs your query, diffs it against the stored snapshot with SQL EXCEPT, and emits a JSON delta on the pgr channel. The proxy receives the NOTIFY and routes it to the WebSocket connections subscribed to that query_id.
Connecting from a client
ws://127.0.0.1:8080/ws/{query_id}
ws://127.0.0.1:8080/ws/{query_id}?token={jwt}
The proxy confirms the subscription, then streams deltas:
{"type": "subscribed", "query_id": "active_orders"}
{"query_id": "active_orders", "seq": 17, "inserted": [/* … */], "deleted": [/* … */]}
The TypeScript SDK manages this socket, applies deltas to a local snapshot, and re-fetches on overflow — see the SDK and React hook pages. The complete message catalogue lives in Wire Format.
Registering a query
Subscriptions are registered in PostgreSQL with pgr.subscribe(). Because the stored SQL is later re-executed by SECURITY DEFINER triggers, never grant pgr.subscribe directly to app roles — expose it through a purpose-built SECURITY DEFINER wrapper with a fixed query template and a server-derived query_id/audience. The full reasoning and the canonical wrapper pattern are in the Security Model; see The Subscription API for the function reference.
Auth & RLS integration
pgStack adds two things on top of the bare extension:
- Audience-bound subscriptions. A subscription registered with an
audience(e.g.{"sub":"42"}) is only served to a client whose JWT satisfies it; everyone else is rejected with403at WS-connect, before any data flows. This closes capability leakage even if a client guesses thequery_id. See Row-Level Security for the worked example. - Defence in depth with RLS. The snapshot is filtered by the query's
WHEREat subscribe time, and your tableROW LEVEL SECURITYpolicies still apply when the proxy is not usingservice_role.
Next steps
- pg_reactive — the extension — the full engine documentation
- The Subscription API —
pgr.subscribe, modes, lifecycle - Wire Format — every message the channel emits
- Security Model — the privileged-API footgun and the safe pattern