Built-in skills reference

At a glance

  • What — the seven skills shipped with the engine. Attach one to a node (skill=<route>) to make it active: it runs when traversal reaches the node.
  • They share — Event Script data-mapping syntax (source -> target) and the same state-machine namespaces (input.*, model.*, output.*, {node}.result).
  • One skill per node. A node returns a decision to the engine — next (follow the connection), a node name (jump), or .sink (pause this path).
Skill Use it to…
graph.data.mapper copy/transform data between namespaces
graph.math compute and branch with fast inline math/boolean
graph.js compute/branch with full JavaScript (slower)
graph.api.fetcher call external HTTP APIs declaratively
graph.extension delegate to a sub-graph or an Event Script flow
graph.join synchronize parallel paths
graph.island mark an isolated/organizational node

graph.data.mapper

Copies and transforms data between state-machine namespaces. The workhorse for shaping inputs and building the response.

skill=graph.data.mapper
mapping[]=source -> target

Sources/targets use input.*, model.*, output.*, or a node name (its properties); text(...), int(...) etc. inject constants. Example:

create node my-mapper
with properties
skill=graph.data.mapper
mapping[]=input.body.hr_id -> employee.id
mapping[]=input.body.join_date -> employee.join_date

graph.math

Fast inline math and boolean evaluation for computation and decision-making. Prefer this over graph.js when you only need simple expressions. Statements run in order; five types:

Statement Purpose
COMPUTE evaluate a math expression → the node's result
IF boolean decision → jump to a node (THEN/ELSE)
MAPPING data-map source → target (no curly braces)
EXECUTE run another graph.math node
RESET clear a node's "seen" flag so it can run again
skill=graph.math
statement[]=COMPUTE: amount -> (1 - {input.body.discount}) * {book.price}
statement[]='''
IF: (1 - {input.body.discount}) * {book.price} > 5000
THEN: high-price
ELSE: low-price
'''

{variable} resolves a value from input.*, model.*, or a node property into the expression. An IF returning a node name overrides natural traversal; returning next keeps it. Optional for_each[] iterates a statement block; NEXT:/DELAY: control flow and timing.

Gotchas: a node runs once (guard against loops) unless you RESET it — an advanced, use-with-care feature; a node may not contain only MAPPING statements (use the data mapper).

graph.js

Same statement model as graph.math, but expressions run as full JavaScript on GraalVM — more flexible, slower.

skill=graph.js
statement[]=COMPUTE: amount -> (1 - {input.body.discount}) * {book.price}

Gotchas: capped at 50 instances per deployment (it uses kernel threads); reach for graph.math unless you need real JavaScript.

graph.api.fetcher

Calls external HTTP APIs declaratively, driven by data-dictionary and provider nodes. Supports response caching and bounded fork-join concurrency.

skill=graph.api.fetcher
dictionary[]=<data-dictionary-node>     # one or more
input[]=input.body.person_id -> person_id
output[]=result.name -> output.body.name
for_each[]=<array> -> model.<var>        # optional: iterate
concurrency=3                            # optional: 1–30, default 3
exception=<error-handler-node>           # optional

Worked example (fetch a person's name and address):

create node fetcher
with type Fetcher
with properties
skill=graph.api.fetcher
dictionary[]=person-name
dictionary[]=person-address
input[]=input.body.person_id -> person_id
output[]=result.name -> output.body.name
output[]=result.address -> output.body.address

The result lands at {node}.result. Gotcha: identical requests (same URL + method + input) are deduplicated into a single HTTP call. See Composing the layers for the dictionary/provider setup this skill depends on.

graph.extension

Delegates to another graph model or an Event Script flow, so you can compose larger capabilities and reuse logic.

skill=graph.extension
extension=<graph-id>          # a sub-graph …
extension=flow://<flow-id>    # … or an Event Script flow (note the flow:// prefix)
input[]=input.body.person_id -> person_id
output[]=result -> output.body

Sub-graph example (reuse a deployed graph):

create node performance-evaluator
with type Extension
with properties
skill=graph.extension
extension=evaluate-sales-performance
input[]=input.body.department_id -> id
output[]=result.sales_performance -> output.body.sales_performance

This is the seam between the semantic layer and the composable layer beneath it — see Composing the layers.

graph.join

A synchronization barrier for parallel branches. It returns next only when all connected upstream nodes have completed, and .sink (pause) until then.

skill=graph.join
connect fetch-name to join with done
connect fetch-address to join with done
connect join to combine with proceed

Gotchas: needs at least two predecessors to be meaningful; it is the explicit fork-join mechanism — without it, traversal proceeds as branches complete.

graph.island

Marks an isolated node: it always returns .sink, so traversal does not continue through it. Used to organize non-executable configuration (e.g. grouping data-dictionary and provider nodes) or to park a sub-graph that isn't wired in yet.

skill=graph.island
connect root to dictionary with contains
connect dictionary to person-name with data
connect dictionary to person-address with data

See also