Scriptable Graphs
Generate and update Schematify graphs with TypeScript scripts
Scriptable Graphs let you define a Schematify graph in TypeScript and publish it with the CLI. Instead of hand-editing JSON, you write code that reads source systems such as APIs, databases, cloud inventories, config files, and CI metadata, then turns them into a current graph.
The graph becomes a repeatable build artifact rather than a drawing maintained by hand.
When to use scripts
Use a script when your graph should be generated from source data:
- infrastructure discovered from cloud or Kubernetes APIs
- service maps assembled from repositories or deployment manifests
- database schemas read from live databases or migration files
- dependency graphs generated from package metadata
- operational views that include live status, metrics, or labels
For one-off models, the Document Manager editor is often enough. For graphs that should stay accurate over time, use Scriptable Graphs.
Running a script
Graph scripts run through the Schematify CLI:
schematify run graph.ts
schematify run executes the script in a sandboxed V8 isolate. When the script calls publish(), the graph is validated and published to your Schematify server.
Use dry-run to test without writing to the server:
schematify dry-run graph.ts
In dry-run mode, graph publishes and channel sends are printed to stdout as JSON lines instead of being sent to Schematify.
For long-running scripts, such as scripts that continuously publish channel updates, add a duration limit while testing:
schematify dry-run graph.ts --max-duration 10s
schematify run graph.ts --max-duration 5m
For larger scripts, increase the isolate memory limit:
schematify run graph.ts --memory 256
A minimal graph
Scriptable Graphs provide global builder functions: graph, node, channel, from, and channelPublisher. You do not need to import them in a script run by the CLI.
async function main() {
const doc = graph("550e8400-e29b-41d4-a716-446655440000")
.label("Production Architecture")
.description("Generated from deployment metadata")
.children([
node("frontend")
.label("Frontend")
.type("microservices/service")
.links(["api"]),
node("api")
.label("API")
.type("microservices/service")
.links(["database"]),
node("database")
.label("Database")
.type("microservices/database"),
]);
await doc.publish();
}
void main();
This compiles to a normal Schematify document. You can still pull, validate, and inspect the generated document using the regular CLI commands.
Builder API
graph(id)
Creates the document builder. Use a stable UUID for the graph ID so repeated runs update the same graph.
Common methods:
graph("550e8400-e29b-41d4-a716-446655440000")
.label("Graph label")
.description("Optional description")
.staleAfter(30_000)
.children([...]);
doc.publish() compiles, validates, and publishes the generated document.
node(id)
Creates a node. Node IDs are path segments, so they must be unique among siblings. Nested children become paths like cluster/namespace/pod.
Common methods:
node("api")
.label("API Service")
.type("microservices/service")
.attributes({ owner: "platform", environment: "prod" })
.links(["database"])
.children([...]);
Links are written as target paths. From a top-level node, links(["database"]) connects to the top-level database node. To link to nested nodes, use full paths such as cluster-a/default/api-pod.
channel(id)
Declares a real-time data slot on a node. Channels define what live values the node can receive later.
node("api")
.channels([
channel("status").label("Status").default("base/healthy"),
channel("latencyMs").label("Latency").staleAfter(15_000),
]);
from
Connects render or status fields to a channel, attribute, or literal value.
node("api")
.channels([channel("status").default("base/healthy")])
.status({ type: from.channel("status") });
Publishing live updates
A script can publish the graph document, then continue sending channel values to open Graph Viewer sessions.
async function main() {
const doc = graph("550e8400-e29b-41d4-a716-446655440000")
.label("Live Services")
.children([
node("api")
.label("API")
.channels([channel("status").label("Status").default("base/healthy")])
.status({ type: from.channel("status") }),
]);
await doc.publish();
const pub = channelPublisher(doc.id);
setInterval(async () => {
const healthy = Math.random() > 0.2;
pub.set("api", { status: healthy ? "base/healthy" : "base/warning" });
await pub.send();
}, 1000);
}
void main();
Use --max-duration when testing loops:
schematify dry-run live-services.ts --max-duration 10s
Typical workflow
- Write a script that builds a graph from known data.
- Run
schematify dry-run graph.tsuntil the output looks right. - Run
schematify run graph.tsto publish it. - Schedule the script in CI, cron, or your own automation so the graph stays current.
- Add channels and
channelPublisherwhen you need live status or metric updates.
Best practices
- Use stable UUIDs for graph IDs. Regenerating a new ID on every run creates new documents instead of updating the existing one.
- Keep node IDs stable and machine-friendly. Use labels for human-readable names.
- Model containment with
children()and cross-cutting dependencies withlinks(). - Prefer generating from authoritative sources rather than copying data into the script.
- Test with
dry-runbefore publishing from automation. - For long-running scripts, set explicit
--max-durationduring development.
What’s next
- CLI: command reference for
run,dry-run,publish, and document management. - Document Format: the JSON structure generated by scripts.
- AI Integration: use agents to generate and maintain graph scripts.