Realtime
How ProcessFlow delivers live updates to the browser using PostgreSQL LISTEN/NOTIFY and Server-Sent Events (SSE).
Realtime Architecture
ProcessFlow delivers live UI updates — such as new task assignments, process instance progress, and team membership changes — without WebSockets or a third-party service. The stack is PostgreSQL LISTEN/NOTIFY on the server side and Server-Sent Events (SSE) on the client side.
PostgreSQL LISTEN/NOTIFY
Each relevant table (e.g. flow_element_instance, profile_team, role) has an AFTER INSERT/UPDATE/DELETE trigger that calls pg_notify() with a channel name and a JSON payload:
The available channels are:
| Channel | Fires when… |
|---|---|
flow_element_instance_changes | A task is created, updated, or completed |
role_changes | A team role is created or modified |
profile_team_changes | A member joins or leaves a team |
profile_role_team_changes | A member's role assignment changes |
invitation_changes | An invitation is created or revoked |
Server-Sent Events Endpoint
/api/realtime (src/app/api/realtime/route.ts) opens a persistent SSE stream per browser connection:
- The handler reads the requested channels,
teamId,email, andprofileIdfrom the query string. - It subscribes to the requested channels on a module-level
PgListenerSingleton(lib/pg-listener.ts), which maintains a singlepgclient connection withLISTENcommands across all SSE subscribers. - Incoming
pg_notifypayloads are filtered server-side byteam_id/email/profile_idbefore being forwarded to the client. - When the browser disconnects, the handler unsubscribes and the stream closes cleanly.
Client Hook
Components subscribe to realtime events via the useRealtimeSubscription hook (src/hooks/useRealtimeSubscription.ts):
The hook opens an EventSource on mount, parses each SSE message, and calls onEvent for every non-heartbeat event. It closes the connection on unmount.
Why SSE over WebSockets?
- Simpler infrastructure: SSE is plain HTTP — no upgrade handshake, no separate WS server.
- Works with Next.js serverless/edge model: A
ReadableStreamresponse is all that's needed. - One-directional by design: The server pushes events; clients respond via Server Actions or API routes. No bidirectional channel is required.
- pg_notify is sufficient: PostgreSQL's built-in notification system handles all use cases without an additional message broker.