WebSocket & SSE

WebSocket API Overview

RESTHeart Ulabase

RESTHeart embeds a WebSocket server that exposes MongoDB’s Change Streams to web browsers and to any other HTTP/WebSocket client. With it you can build web or mobile apps that are notified of data changes in real time, without polling the server for updates.

WebSockets and Change Streams

Two standards do the work. The first is the transport between the client and RESTHeart:

The WebSocket API is an advanced technology that makes it possible to open a two-way interactive communication session between the user’s browser and a server. With this API, you can send messages to a server and receive event-driven responses without having to poll the server for a reply.

— MDN Web Docs
The WebSocket API

The second is how MongoDB tells RESTHeart what changed:

MongoDB’s Change streams allow applications to access real-time data changes without the complexity and risk of tailing the oplog. Applications can use change streams to subscribe to all data changes on a single collection, a database, or an entire deployment, and immediately react to them. Because change streams use the aggregation framework, applications can also filter for specific changes or transform the notifications at will.

— MongoDB Manual
Change Streams

RESTHeart joins the two: it opens the change stream on MongoDB and pushes every change event down to the WebSocket clients watching it.

Exposing a Change Stream

Change streams are not opened by the client at will: they are declared by the developer as the streams collection metadata, and RESTHeart binds each definition to a URI under /<db>/<collection>/_streams.

{ "streams": [
    { "uri": "all",
      "stages": [
        { "_$match": { "_$or": [ { "operationType": "insert" }, { "operationType": "update" } ] } }
      ]
    }
]}

With the stream all defined on the collection messages, clients connect via WebSocket to ws://mydomain.com/messages/_streams/all and receive real time notifications of the data changes occurring in that collection.

The stages are a MongoDB aggregation pipeline over the change events, so they can filter and transform the notifications before they are sent — here, only inserts and updates are notified. See Change Streams for the full definition format, including why the operators are written $match and $or when the metadata is submitted through the REST API.

Filtering per Client

The same stream can serve different data to different clients, and the way to do it is notify_when: a condition in the stream definition, evaluated per connected client against a variable the client binds at connection time. All the clients of the stream share one MongoDB cursor, and each receives only the events that match its own value:

{
  "uri": "mine",
  "stages": [ { "$match": { "operationType": { "$in": ["insert", "update"] } } } ],
  "notify_when": { "fullDocument::owner": { "$var": "n" } }
}
GET /messages/_streams/mine?n=Andrea HTTP/1.1

Stages accept variables too, but a variable in a stage changes the pipeline, and a different pipeline is a different cursor: a stream that filters in its stages with { "$var": "n" } opens one MongoDB change stream per distinct value of n. Keep the stages fixed, the same for everyone, and put every client-bound variable in notify_when. See notify_when and Using Variables.

Permissions Apply to a Stream

A change stream is a read, and the caller’s permission governs it as it governs the equivalent GET. Since 9.8.2:

  • the readFilter of the matching permission is applied to the events: a caller limited to their own documents is notified about their own documents only, and events that carry no document — deletes — are not delivered when the filter is about the document’s own fields;

  • an exclusion projectResponse removes those properties from the event, including from updateDescription.updatedFields;

  • a readFilter that cannot be applied to an event ($expr, $where, $text) and an inclusion projectResponse, which would strip the event’s resume token, refuse the subscription with 403.

Warning
Before 9.8.2 neither was applied. A caller who could open the stream received every event on the collection, whatever their readFilter, and with the properties projectResponse hides. If you run an older version, restrict who may reach /_streams until you upgrade.

Filtering per client with notify_when is a convenience for the client, not a boundary: the variable it binds comes from the client’s own query string. See notify_when.

No More Polling

Because RESTHeart pushes the events, a client learns about a change the moment it happens — no polling loop, no requests that come back empty, no delay between the write and the notification.

Requirements and Lifecycle

Note
Change streams require MongoDB configured as a Replica Set.
Note
When the streams collection metadata is modified, or the collection or the db is deleted, all related WebSocket connections are closed and the change streams are consequently updated.

WebSocket or SSE?

The same change streams are also available over Server-Sent Events, at the same URL, with Accept: text/event-stream instead of a WebSocket upgrade. Both transports share the same MongoDB cursor; SSE reconnects and resumes on its own, WebSocket gives you a full-duplex channel. The SSE vs. WebSocket table compares them feature by feature.

Next Steps

  • WebSocket API Tutorial — a step-by-step walkthrough, from creating the collection to receiving events in the browser

  • Change Streams — the streams metadata and stream definition format

  • Using Variables — how a client binds the variables of notify_when, and why they do not belong in the stages