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 keeps a single in-memory cache — rootData — that is shared across every access on the server Proxy. Every time data is fetched from the server, it is merged into rootData at the correct path. Subsequent accesses to the same path, or any child of an already-fetched path, are served directly from that cache without touching the network.

How the cache works

rootData is a plain nested JavaScript object that mirrors the structure of your server’s data tree. When a fetch completes, processBatchData writes the response into rootData at the matching path. Because the entire response is stored — not just the specific leaf you requested — child paths are populated as a side-effect.
// Fetches posts.page1 from the server — stores { title, body } in cache
const post = await server.posts.page1;

// Resolved from cache — no HTTP request
const title = await server.posts.page1.title;
In the example above, the first await fetches the full posts.page1 object and writes both title and body into rootData.posts.page1. The second await finds the data already present and returns immediately. This means the granularity of your fetches naturally determines how much is pre-populated: fetching a parent node is always more efficient than fetching individual children one by one.

Deduplication

dragonJSON uses a pendingFetches Map, keyed by dot-separated path string, to prevent duplicate in-flight requests. If two separate awaits request the same path at the same time, only one HTTP request is sent. Both awaits share the same Promise and resolve together when it completes.
// Only one HTTP request is made — both awaits share the same pending promise
const [a, b] = await Promise.all([
  server.posts.page1,
  server.posts.page1,
]);
Once the fetch settles, its entry is removed from pendingFetches. A later access to the same path will find the data in rootData and return from cache.

Mutation-driven invalidation

All three mutation methods — $set, $add, and $remove — POST to the server and expect a response of the form:
{ "invalidate": ["path.to.refresh"] }
The client processes each path in the invalidate array immediately after the mutation:
  • For objects, the path is marked { __next: true }, turning it into a deferred placeholder. The next access to that path triggers a fresh fetch.
  • For removes ($remove), the key is deleted from rootData entirely, so the cache reflects the deletion immediately.
After invalidation, any $on listeners registered on those paths are fired automatically.
await server.posts.$set("page1", { title: "Updated" });
// server returns { invalidate: ["posts.page1"] }
// posts.page1 is now marked stale in rootData

// Next access triggers a fresh fetch
const title = await server.posts.page1.title;
This model keeps the cache consistent without requiring the client to know anything about your data model — the server simply tells the client exactly which paths changed.

Manual refresh

$refresh() immediately marks a path as stale, independently of any mutation. It is useful when you know data has changed from an external source — a server-sent event, a polling check, or anything outside the normal mutation flow.
await server.posts.$refresh();
Calling $refresh() on an object path sets it to { __next: true }. The next await on that path (or any child) will fetch fresh data from the server. $refresh() also fires any $on listeners at that path with initiator: "refresh".

Checking cache state

dragonJSON exposes two methods for inspecting whether a path is available locally:

$loaded()

Returns true synchronously (via async wrapper) if the path is present in rootData and not marked __next. No network call is made. Use this to conditionally skip a fetch when you’re confident data is already warm.

$exists()

Fetches the path if it isn’t cached, then returns true if the path resolves to a non-undefined value. Use this when you need to check existence but are willing to pay a network cost.
if (await server.posts.page1.$loaded()) {
  // already in cache, instant — no network call
}

if (await server.posts.page2.$exists()) {
  // fetched if needed, resolves true if the path exists on the server
}
$loaded() checks the local cache only. A path that has never been fetched, or one that has been invalidated (marked __next), will return false even if the data exists on the server.

Build docs developers (and LLMs) love