Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/michael-tiger-2010/dragonjson/llms.txt

Use this file to discover all available pages before exploring further.

dragonJSON includes a built-in event system that fires callbacks whenever data at a given path is mutated. Every $set, $add, and $remove call dispatches an event after the cache has been updated, so your listeners always receive a reference to the already-invalidated path. When liveInvalidation is configured, incoming relay messages from other clients trigger the same listeners — one subscription covers both local and remote mutations.

$on — subscribe to mutations

server.path.$on(type, key, callback, mode?)
type
"add" | "remove" | "set" | "*"
required
Which mutation operation to listen for. Use "*" to match all three.
key
string
required
The child key to match. Pass a specific string to listen for changes to that key only, or "*" to match any key (including operations on the node itself).
callback
function(event)
required
Called whenever a matching event fires. Receives a single event object — see Event object below.
mode
"direct" | "bubble"
Controls which paths can trigger this listener. Defaults to "direct". See Direct mode and Bubble mode for details.

Event object

The object passed to your callback contains the following fields:
initiator
"add" | "remove" | "set" | "*"
The operation that triggered the event.
key
string
The child key that was affected. "*" when the operation targeted the node itself rather than a named child.
path
string[]
Array of path segments identifying the origin of the mutation. For example, a mutation at server.posts.page1 produces ["posts", "page1"].
data
Proxy
A lazy proxy pointing to the affected path. Awaiting it re-fetches from the server if the path has been marked stale by the mutation.
invalidate
string[]
The array of dot-separated paths the server declared stale in its mutation response — the same value that was used to update the cache.

Direct mode (default)

In "direct" mode a listener fires only when an event originates on the exact path where it was registered. This is the default and is the right choice when you care about a specific location in the tree:
// Only fires when something is added directly under posts
server.posts.$on("add", "*", (e) => {
  console.log("New post added with key:", e.key);
});

// Only fires when posts.page1 is removed
server.posts.$on("remove", "page1", onPage1Removed);

Bubble mode

In "bubble" mode a listener fires when an event originates at the registered path or any descendant — analogous to DOM event bubbling. Use this to monitor an entire subtree from a single registration point:
// Fires on ANY mutation anywhere under posts
server.posts.$on("*", "*", (e) => {
  console.log("Something changed under posts:", e.initiator, e.key);
}, "bubble");
The path field on the event tells you exactly where in the tree the mutation originated, so you can filter further inside the callback if needed.

$off — unsubscribe

Remove a listener with $off. All four arguments must match exactly the values passed to $on:
server.posts.$off("add", "*", myHandler);
server.posts.$off("add", "*", myHandler, "bubble");
If the mode argument is omitted it defaults to "direct", matching the $on default. An anonymous function passed inline to $on cannot be unsubscribed — always store the callback reference.

Events from live sync

When liveInvalidation is configured at client init, incoming relay messages received from other clients also trigger $on listeners — without any extra code on your part. The cache is updated and listeners fire exactly as if the mutation had been performed locally:
const [server] = dragonJSON("https://api.example.com/data", {
  liveInvalidation: {
    sendRelayMessage: (msg) => socket.emit("invalidate", msg),
    onReceiveCallback: (cb) => socket.on("invalidate", cb),
  },
});

// This listener fires for both local mutations AND remote ones
server.posts.$on("add", "*", (e) => {
  console.log("Post added (by anyone):", e.key);
});
Received relay messages are never re-relayed, so there are no feedback loops. For full details on the live sync configuration and relay message shape, see Live Sync.
Use type: "*" and key: "*" with mode: "bubble" at the root to create a global change log for debugging:
server.$on("*", "*", (e) => {
  console.log("[dragonJSON]", e.initiator, e.path.join("."), e.key);
}, "bubble");

Build docs developers (and LLMs) love