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 the CLI’s sandbox. 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. A live publish() marks that graph instance as Scriptable, so its structure can only be replaced by later script publishes. Schematify can still display, share, delete, and copy it. Use Copy to edit in the Document Manager when you want an independent editable graph.
You can still pull, validate, and inspect the generated document using the regular CLI commands. The saved graph carries an authoring: { type: "script" } marker which Schematify adds during publishing.
Builder API
graph(id)
Creates the document builder. Use a stable graph ID so repeated runs update the same graph. A UUID is a convenient choice for a new 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
- Keep graph IDs stable. Generating a new ID on every run creates a document 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.