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.

In most static data trees, the client can determine whether a key exists based on what the parent already returned. But in collections with user-generated or dynamic keys — user profiles, content posted at runtime, tenant-specific data — the client cannot know all keys in advance. dragonJSON handles this with two reserved flags, __more and __next, that the server sets on response objects to signal how the client should handle child access.
Keys whose names start with __ are reserved by dragonJSON. Don’t use them as actual data keys in your store.

The __more flag

When the server returns { "__more": true } for a node, dragonJSON treats it as an open-ended collection. Instead of returning undefined for unknown keys, the client issues an on-demand fetch for any property accessed under that node:
// Server response for GET ?path=users
{ "__more": true }
Client usage:
// users has __more: true — fetches users.alice on demand
const alice = await server.users["alice"];
const bob = await server.users["bob"];
Each access to an unknown key under a __more node triggers a GET ?path=users.alice request. The result is cached normally, so repeated access to the same key is instant after the first fetch.

The __next flag

For subtrees you want to defer entirely, return { "__next": true }. The client stores it as a placeholder and re-fetches the full node when any child is first accessed:
// Server response for GET ?path=posts — defer the subtree
{ "__next": true }
Unlike __more, __next signals that the node has known children that simply haven’t been sent yet. The client will fetch the real data for that path the first time someone awaits a property beneath it.
// posts was returned as { __next: true }
// Accessing page1 triggers GET ?path=posts, which resolves the full posts object
const post = await server.posts.page1;

Combining flags

A node can carry both flags simultaneously, indicating it has deferred known children and may have additional dynamic keys beyond those:
// GET ?path=posts
{ "__more": true, "__next": true }
When the client fetches the node, it will also accept on-demand fetches for any key that wasn’t present in the resolved response.

Implementing in the Node.js server

To tell the client that a collection is open-ended, return __more: true from your GET handler for that path. Here is how to add it to the Node.js reference server:
// In your GET handler, return __more for open-ended collections
if (segments.length === 1 && segments[0] === "users") {
  return send(res, 200, { __more: true });
}
When the client then accesses server.users["charlie"], it sends GET ?path=users.charlie and your server handles that specific lookup:
if (segments.length === 2 && segments[0] === "users") {
  const user = getByPath(store, segments);
  if (user === undefined) return send(res, 404, { error: "Not found" });
  return send(res, 200, user);
}

Checking existence under wildcard nodes

Because __more nodes accept arbitrary key access, a fetch for a non-existent key will hit the server and return a 404. Use $exists() to check before committing to a full fetch:
const exists = await server.users["charlie"].$exists();
if (exists) {
  const user = await server.users["charlie"];
}
$exists() resolves true if the path can be fetched successfully and false on a 404 or network error — it never throws. By contrast, $loaded() only checks the local cache without making a network request, which is useful when you have already prefetched and want to avoid redundant calls.

Build docs developers (and LLMs) love