Realtime events¶
Firebird can tell its clients that something happened: a trigger or procedure runs
POST_EVENT 'order_placed', and when its transaction commits every attachment listening for that
name is notified. FirebirdREST forwards these notifications to HTTP clients as
Server-Sent Events, so a page or
a service can react to a change without polling. PostgREST has no equivalent; Supabase provides it
through a separate service.
Exposing events¶
Events carry no data and Firebird checks no privilege before delivering them, so FirebirdREST only streams the names you list:
db-events = "order_placed, stock_changed"
db-events-roles = "web_user" # optional: only these roles may listen
Without db-events the endpoint is off (404, FBRST135). Without db-events-roles any
authenticated role may listen; the anonymous role may listen only when it is listed. As for any
request, the role must be granted to the authenticator user.
In the database, post the event where the change happens:
The example database
posts order_placed from the place_order procedure and has a notify(name) procedure that posts
any event, for trying things out.
Listening¶
retry: 3000
: listening order_placed,stock_changed
: keepalive
id: 1
event: order_placed
data: {"name":"order_placed","count":1}
- The path names one event or several, comma separated. Names are matched as Firebird stores
them:
stock_changedandSTOCK_CHANGEDare the same event. - Every notification is one SSE message:
eventis the name,datarepeats it withcount, the number of posts Firebird merged into this notification (one transaction posting the same event ten times is delivered once, withcount: 10), andidcounts the messages of the stream. - A comment line (
: keepalive) is sent every 15 seconds so proxies and clients keep the connection open.retry: 3000tellsEventSourceclients to reconnect after 3 seconds. - The stream is not subject to
server-request-timeout; it ends when the client closes it or the server shuts down (then withevent: erroranddata: {"message":"server shutting down"}). - An event that isn't exposed is
404(FBRST136); a role that may not listen gets403(FBRST137,401when anonymous).
A client hears that something changed, not what: it then reads the data it needs through the API, under its own role and grants. That keeps events safe to expose to many roles: an event name is the only thing they learn.
Browsers' EventSource can't send an Authorization header. Use fetch with a streaming body
instead, as firebirdrest-js does, or put the token in
a cookie that a proxy turns into the header.
How it works¶
One Firebird attachment listens for all exposed events and fans the notifications out to the
HTTP streams in process: a thousand listeners cost the database one connection. If the
connection to the database is lost the server resubscribes every 30 seconds; streams opened
meanwhile get event: error with "not listening" and should reconnect. A stream that isn't
read fast enough loses notifications rather than slowing the others (a comment in the stream
says how many).
With several FirebirdREST instances each listens by itself, so a client connected to any of them hears every event.
With firebirdrest-js¶
const channel = client.channel('order_placed', 'stock_changed')
.on('order_placed', (e) => refreshOrders()) // e.name, e.count
.on('*', (e) => console.log('event', e.name))
.subscribe((status) => console.log(status)) // "open", "closed", "error"
// later
channel.unsubscribe()
The channel reconnects by itself with a growing delay while the server is unreachable.
Metrics¶
With db-events set, /metrics reports open streams, notifications received by event,
notifications delivered, and times the database subscription was lost
(monitoring).