Realtime
Subscribe to changes in a collection over a WebSocket. Every event is re-checked against the
collection’s read policy before it reaches you.
With the SDK
Section titled “With the SDK”const posts = client.collection('posts');
const stop = posts.subscribe('insert', (event) => { console.log(event.type, event.documentId, event.document);});
// laterstop();subscribe(event, callback) takes 'insert', 'update', 'replace', 'delete' or '*'.
One lazy WebSocket per client is opened on the first subscription and multiplexed across all
of them; unsubscribing the last one closes it.
There is no automatic reconnect in this version. A closed socket reopens on the next
subscribe() call.
The raw protocol
Section titled “The raw protocol”Endpoint: GET /api/v1/realtime, upgraded. A non-upgrade request gets
426 WebSocket upgrade required.
Client → server:
{ "type": "auth", "token": "<access token>" }{ "type": "subscribe", "id": "sub1", "collection": "posts", "events": ["insert","update"] }{ "type": "unsubscribe", "id": "sub1" }Server → client:
{ "type": "auth_ok" }{ "type": "subscribed", "id": "sub1" }{ "type": "unsubscribed", "id": "sub1" }{ "type": "event", "subscription": "sub1", "event": { "type": "insert", "collection": "posts", "documentId": "…", "document": { … } } }{ "type": "error", "code": "FORBIDDEN", "message": "…", "id": "sub1" }The first message must be auth — anything before it gets UNAUTHENTICATED with the
message “send { type: “auth”, token } first“. Re-sending auth renews the token on a live
socket.
events is optional and defaults to all four.
Error codes
Section titled “Error codes”INVALID_MESSAGE · AUTH_FAILED · UNAUTHENTICATED · DUPLICATE_ID ·
UNKNOWN_SUBSCRIPTION · NOT_FOUND · FORBIDDEN · TOKEN_EXPIRED · CONNECTION_LIMIT
Authorization, per event
Section titled “Authorization, per event”This is the guarantee that matters: the read filter is re-evaluated against every single event, not once at subscribe time. A document that stops matching your filter stops producing events for you, mid-stream.
Anything that cannot be proven visible is silently dropped — not an error, because an error would itself reveal that something changed.
Two consequences:
deniedReadFieldsare stripped from post-images before emit.- Subscribing requires an existing policy with a matching
readrule; otherwise you getFORBIDDEN. Reserved collection names answerNOT_FOUND, matching the REST surface.
Deletes are id-only
Section titled “Deletes are id-only”A delete event never carries document — after the fact there is nothing to filter against.
Without change-stream pre-images configured, delete events reach only subscribers whose
read filter is unrestricted. If a project must deliver deletes to filtered subscribers, enable
pre-images on the collection. For BYO Mongo, that requires the
collMod privilege — without it, byoCapabilities.preImages is false and the project is
created with a warning rather than failing.
Token expiry
Section titled “Token expiry”An expired token pauses delivery: events are dropped and one TOKEN_EXPIRED error is sent.
Re-authenticating on the same socket resumes it. The SDK does this transparently — it catches
the code, refreshes, and re-auths.
One project per socket
Section titled “One project per socket”The first successful auth pins the project. A renewal token for a different project is
rejected.
Requirements and limits
Section titled “Requirements and limits”MongoDB must be a replica set. Change Streams are a replica-set feature; a standalone
mongod cannot serve realtime. The Docker Compose setup brings up a single-node replica set
for exactly this reason.
On hosted deployments, a project at its live-connection cap answers CONNECTION_LIMIT and
closes with code 1008. Note that counter is per server instance, so a multi-instance
deployment effectively multiplies the allowance.