@monlite/realtime
v0.3.0
Published
Networked realtime for @monlite/core: stream live queries and documents to remote clients over SSE, backed by the change feed.
Maintainers
Readme
@monlite/realtime
Networked realtime for @monlite/core — stream
live queries and documents to remote clients (browser, mobile, other services) over Server-Sent
Events, backed by the change feed. Zero extra dependencies (built on node:http + fetch).
npm install @monlite/realtimeThe database stays embedded in your service; this package puts a realtime API in front of it.
Server
import { createDb } from "@monlite/core";
import { realtime } from "@monlite/realtime";
const db = createDb("./app.db", { changefeed: true }); // change feed required
// Single database:
realtime({ db }).listen(8080);
// Or per-tenant + auth (resolve which db from the request):
realtime({
authorize: (req) => {
const tenant = verify(req.headers.authorization); // your auth
return tenant ? { db: dbForTenant(tenant) } : null; // null → 401
},
}).listen(8080);Attach to an existing server/framework instead of listen():
const rt = realtime({ db });
http.createServer((req, res) => {
if (req.url?.startsWith("/realtime")) return rt.handler(req, res);
// ... your other routes
});Client (browser or Node ≥ 18)
import { connectRealtime } from "@monlite/realtime/client";
const live = connectRealtime("https://api.example.com", { token });
// Live query — fires with the snapshot, then on every change
const stop = live
.collection("orders")
.where({ status: "open" })
.orderBy({ createdAt: "desc" })
.onSnapshot(({ results, added, removed, changed, moved }) => render(results));
// Single document (null on delete)
const stopDoc = live.doc("orders", "o-123", (doc) => render(doc));
// Only re-emit when a specific field changes
live.collection("orders").fields(["status"]).onSnapshot(onChange);
stop(); // unsubscribe
live.close(); // unsubscribe everythingClient options: { token, path, reconnectMs, fetch, onError }. onError (default console.error)
receives any server-sent { error } frame — e.g. a watch that failed server-side — so it is never
mis-delivered as a snapshot or a null document.
How it works
- One SSE stream per subscription; the query travels in the URL.
- The server runs
collection.watch()/watchDoc()on the authorized database and pushes eachLiveEvent(init snapshot, thenadded/removed/changed/moveddeltas) down the stream. - The client auto-reconnects with backoff; on reconnect it receives a fresh snapshot (no missed
state). Because writes flow through the change feed,
changes from other processes and from
@monlite/syncare delivered too.
Notes
- Auth & multi-tenancy are your
authorizehook's job — it maps a request to a{ db }. - CORS is
*by default; setcorsto a specific origin (orfalse) in production. - Pairs naturally with the embedded, one-
.db-file-per-tenant model.
