LISTEN / NOTIFY
Postgres' built-in pubsub. The engine ships three helpers — listen, unlisten, notify — that produce CompiledQuery objects you hand to an adapter. Channel names are quoted as identifiers; payloads are bound as parameters.
The three helpers
| Export | SQL |
|---|---|
listen(channel) | LISTEN " |
unlisten(channel) | UNLISTEN " |
unlisten('*') | UNLISTEN * — drop every subscription on this connection. |
notify(channel, payload?) | SELECT pg_notify($1, $2) — payload is bound as a parameter. |
notify uses pg_notify(channel, payload) rather than the bare NOTIFY channel, 'payload'syntax — the function form lets us bind the payload as a real parameter, which the bare form doesn't support.
Subscribing
import { listen, unlisten } from 'zanith'; // Subscribe — runs on whichever connection the adapter hands out.await adapter.execute(listen('user_changes')); // The adapter is responsible for surfacing notifications. With pg, the// underlying client emits a 'notification' event:const client = adapter.rawClient(); // pg-specific helperclient.on('notification', (msg) => { console.log(`channel=${msg.channel} payload=${msg.payload}`);}); // Stop listening:await adapter.execute(unlisten('user_changes'));// Or drop everything on this connection:await adapter.execute(unlisten('*'));Publishing
import { notify } from 'zanith'; // Wake every subscriber to 'user_changes'. Payload is optional.await adapter.execute(notify('user_changes')); // Carry a payload — bound as a parameter, not interpolated.await adapter.execute(notify('user_changes', JSON.stringify({ userId: 42, action: 'updated' }))); // Inside a transaction — recipients see the notification only after COMMIT.await db.transaction(async (tx) => { await tx.user.update({ where: { id: 42 }, data: { lastSeen: new Date() } }); await tx.executeRaw(...buildSqlForNotify('user_changes', '{"userId":42}'));});Postgres queues notifications until the enclosing transaction commits. If you want the wake-up to fire alongside the data change, send the notify from inside the same transaction.
Payload size
Postgres caps notification payloads at 8000 bytes. Use the payload to carry an id and a hint, then have the subscriber fetch the full record — never try to ship a row through it.
// Right.notify('user_changes', JSON.stringify({ id: userId, op: 'updated' })); // Wrong — will throw above 8KB and is fragile even below.notify('user_changes', JSON.stringify(fullUserRecord));