v0 · Developer Preview Frond is under active development. APIs may change between releases.

Identity and keys

A node’s identity is a graph node id of the form tag:key. The tag is set on the spec; the key is computed from args every time a node is requested. Same tag and same key produce the same id, which resolves to the same node.

runtime.client.node(ProfileNode, { userId: "u_42" }).nodeId;
// -> "resources/profile:v1:{\"userId\":\"u_42\"}"

The tag

The tag names the kind of node, not an instance. Set it on the spec with Frond.tag(value).

static readonly spec = Frond.resourceSpec<ProfileSpec>({
  tag: Frond.tag("resources/profile"),
  // ...
});
  • Use path-like values: resources/profile, not resources/profile/u_42.
  • The tag is part of the id. Renaming it changes the id of every node built from the spec.
  • Two specs must not share a tag.

The key

A node declares a key(args) function. The runtime runs it, then canonicalizes the result into the string half of the id.

key: (args) => Frond.Key.structure({ userId: args.userId }),

The function picks the parts of args that define identity and returns a branded key. Use Frond.Key.structure(...) for JSON-shaped structured keys and Frond.Key.singleton() for singleton nodes.

Warning: key must be pure and deterministic — no clock, no random, no environment, no Effect services, no async. A non-deterministic key (for example, one that includes Date.now()) produces a new id on every call, and therefore a new node and a new fetch.

Canonicalization

The runtime does not compare key values directly. It serializes the value with canonicalKey and compares the resulting string.

RuleBehavior
Object key orderIgnored. Object keys are sorted before stringifying, so { a: 1, b: 2 } and { b: 2, a: 1 } produce the same key.
Array orderSignificant. [1, 2] and [2, 1] are different keys. Sort first if order is not part of identity.
Allowed valuesJSON-shaped only: string, finite number, boolean, null, undefined, arrays, and plain objects.
PrefixThe canonical string starts with v1:.
Length cap2048 characters (MAX_CANONICAL_KEY_LENGTH).

Inputs that cannot be canonicalized throw:

InputError
A Date, Map, class instance, or functionKeyUnsupportedJsonValueError
NaN, Infinity, or -InfinityKeyNonFiniteNumberError
A canonical string over 2048 charactersKeyTooLongError

Include only the fields that distinguish the node; embedding whole payloads can exceed the length cap.

Failure modes

ModeCauseResultFix
Over-keyingA field varies when it should not (timestamp, request nonce, object reference recreated each render).One logical node fragments into many ids, each with its own fetch and cache.Key on the stable identifier only.
Under-keyingA distinguishing field is omitted (paginated list keyed on filter but not page).Two distinct nodes collapse onto one id; the second read gets the first node’s result.Put every field that defines a distinct instance in the key.

See Args and deps for declaring args and the key function, and Dependencies for how one node’s id becomes another’s edge.


Next: Lifecycle — the states a node moves through once it has an id.