Real-Time

RdbHost supports real-time asynchronous communication between multiple clients to your web-app. The mechanism involves Postgres, as does pretty much everything at RdbHost. That is the RdbHost way.

How

These real-time aspects rely on Postgres's NOTIFY statement. The NOTIFY statement emits a message on a channel, so all listening Postgres processes can receive it. The way RdbHost uses it is similar in spirit, but different in specifics. We require the same process that sends the NOTIFY message to receive it; RdbHost then gathers the NOTIFY messages from the query and redistributes them to all remote clients that are subscribed on the channel 1.

To register your web client on a channel, just send yourself a message on that channel.

Examples

To explain the capabilities, here are a few examples, going from quite simple and limited, to more elaborate and flexible.

Example One

Rdbhost.on('notify-received', function(channel, payload) {
    if (channel === 'abc') {
       do_something_with(payload);
    }    
});

The above code block creates an event handler to handle received messages on channel 'abc'. That on handler is the same for all examples, applies to all, and won't be repeated for each.

var pr = Rdbhost.preauth()
   .listen('abc')
   .broadcast('abc', 'Hello, World')
   .get_data();

This example broadcasts the message 'Hello, World' to all listening clients, on channel 'abc'. If this is the first execution in this session, the request also registers the web-client as a listener.

Note that registering a web client on a channel requires the completion of an SQL query, so the white-listing system can be used to regulate who can listen or send on a given channel. If, for example, you only want logged-in users using the real-time features, add a credentials check to the query.

Example Two

var pr = Rdbhost.preauth()
    .listen('abc')
    .query('NOTIFY abc, %s;')
    .params(['Hello World'])
    .get_data();

This example has exactly the same consequence as the prior, but exposes the SQL. The channel name is part of the white-list entry, but the message is not. The query can only be used to send messages on 'abc' channel.

Example Three

var q = "SELECT pg_notify('abc', message) FROM public_notices;";

var pr = Rdbhost.preauth()
    .listen('abc')
    .query(q)
    .get_data();

This example broadcasts to all connected clients the message column from table public_notices.

Example Four

This last example is more complex, but demonstrates the power of using NOTIFY for real-time communications. The NOTIFY (or pg_notify) can be executed by any SQL code, even that embedded in stored procedures and sub-procedures, including trigger functions triggered by ordinary INSERTS or UPDATES. The trigger is defined with this, executed outside the app itself.

CREATE OR REPLACE function chat_send() RETURNS trigger
LANGUAGE plpgsql
AS $$
  BEGIN
    PERFORM pg_notify('abc', row_to_json(_q)::text) 
      FROM (SELECT NEW.name AS name, NEW.position AS position) AS _q;
    RETURN NULL;
  END;
$$;

CREATE TRIGGER ctrig AFTER INSERT ON users 
   FOR EACH ROW EXECUTE PROCEDURE chat_send(); 

The above block creates a trigger function on a user table, sending a NOTIFY message on channel abc whenever a user record is added.

var q = "INSERT INTO users (name, position) (%s, %s);";
var pr = Rdbhost.preauth()
    .listen('abc')
    .query(q)
    .params(['someone', 'wizard'])
    .get_data();

This code makes neither a .broadcast call, nor an explicit NOTIFY, but it indirectly issues a NOTIFY, (and thus a broadcast message) by inserting into the table so the trigger function previously defined will issue the NOTIFY.
The broadcast message, for this example, is the json string '{"name": "someone", "position": "wizard"}'.

Demos

We provide a demo chat-app, and a set of tests that show working snippets of real-time code; For either of them, open your browser devtools to examine how they work.

demo chat app
examples as tests in QUnit

Footnote

1 As a backend for web-apps, it is not practical for RdbHost to run multiple processes for each database account. Each account only gets a database process when a query has been submitted, and only enough processes for current requests. So, the only process we know will be running for a given account is the one doing the NOTIFY; and that one must catch the NOTIFY.

comments powered by Disqus