Skip to main content
A Data Engine graph is not just a static collection of nodes and connections - it has a defined execution model that determines which nodes run, in what order, and when execution stops. Understanding this model is essential for building graphs that branch, loop, or terminate cleanly.

Roots and ends

Every graph has exactly one root node. The root is where execution begins. From the root, execution follows the connections drawn between nodes, visiting each downstream node and running the operation it defines. Every graph also has at least one end node. An end node is what captures the final output of the graph. A graph without an end node cannot produce a result. End nodes are not only required at the top level. They are also required inside the subgraphs hosted by nodes such as each, switch, and filter, where each subgraph needs at least one end node to supply its result back to the hosting node.

No hanging nodes

Every node in the graph must lead somewhere. Its output must either feed into another node’s input or terminate at an end node. Hanging nodes - nodes whose outputs are not connected anywhere - are not permitted, and the same applies inside every subgraph. The reason is the engine’s port model. Many nodes expose multiple output ports - divide, for example, exposes Decimal, Integer, and Remainder - and the engine cannot infer which output is “the result” without being told explicitly. Rather than making an exception for nodes that happen to have a single output, the same rule applies uniformly: an end node always marks where a value is collected, regardless of how many output ports the upstream node has. Consistency is preferred over the small ergonomic shortcut a single-output exception would allow.
These rules are enforced at execution time, not in the editor. A graph that leaves nodes hanging or omits an end node can still be created and saved without warning, and will only fail once execution is triggered against an entity. Editor-time highlighting of broken graphs is being improved so these issues surface earlier.

Termination

End nodes terminate execution, but the scope of the termination depends on where the end node sits. A top-level end node stops the entire graph. Execution proceeds from the root until it reaches one, and the value present on it at that moment becomes the graph’s output. No further nodes are evaluated, even if other paths exist. When a graph has multiple top-level end nodes, only one of them is reached on any given run - which one depends on the path execution takes through the graph. A subgraph end node terminates only the subgraph it sits in. Reaching it does not stop the parent graph. Instead, the end node supplies the value its subgraph produces back to the hosting node, and the parent graph continues from there. What “the subgraph’s value” means depends on the hosting node: an iteration result for each, a routed-property result for switch, or a truthy/falsy decision for filter.

Branching with switch

The switch node routes dated properties to one of several branches based on a check. The routing happens per dated property in the input: each dated property is matched against the checks independently, picking the first branch whose check conforms or falling through to the Fallback branch when none do. Because every dated property is routed on its own, an input carrying multiple dated properties can end up exercising more than one branch in a single run - whichever branches its properties route to. A branch with no dated property routed to it is simply not executed. Each branch is a subgraph that produces a value for the dated property routed into it. The branch must terminate in an end node; that end node supplies the routed property’s result back to the switch. The switch then merges the per-property results onto its Result output port - which exists precisely so the values produced by branch end nodes have somewhere to land.

Loops with each

The each node iterates over a list, executing a subgraph once per element and collecting the results back into the same shape. The iteration mode is chosen by wiring exactly one of two output ports to the start of the subgraph:
  • Loop Properties iterates over the dated properties in the input. The subgraph runs once per dated property, receiving that whole property as the iteration value.
  • Loop Values iterates over the inner values of each dated property. Use this when a dated property’s value is itself a list: the subgraph then runs once per inner element, and the inner-list iteration is repeated for every dated property in the input.
The subgraph has its own root and its own end nodes, but those end nodes behave differently from the top-level end nodes described above:
An end node reached inside an each subgraph terminates that iteration of the loop, not the parent graph.
In other words, an end node inside an each subgraph signals “this iteration is done, move on to the next element” rather than “the whole graph is done.” Once every element has been visited, execution returns to the parent graph and continues from the each node. filter follows the same shape - a predicate subgraph runs per dated property or per inner value (depending on which port is wired), and its end node supplies the truthy or falsy result that decides whether the element is kept.