Your First Script
This guide walks you through building a complete cloud-arch diagram from scratch. By the end you will have a working topology with two groups, five nodes, three connections, and a two-scenario animation.
The system we model is a simple web application: a browser talking to an API server backed by PostgreSQL, with a Redis cache in front of the database.
Step 1 — Create the topology
Every script starts with a TopologyBuilder instance. Pass true to enable auto-layout (positions are computed automatically); pass false if you want to place groups manually.
const topology = new TopologyBuilder(true);Step 2 — Add a group for the frontend
Groups are rectangular containers that hold nodes. Create one with createGroup.
const clientGroup = topology.createGroup('clients', {
label: 'Clients',
width: 300,
height: 200,
x: 100,
y: 60,
});The first argument is the group’s unique ID. The second argument is configuration: label, dimensions, and position.
Step 3 — Add nodes to the group
Use the fluent GroupBuilder returned by createGroup to add nodes, then call autoResize() so the group expands to fit its contents.
clientGroup
.addProcess({ id: 'browser', label: 'Browser', state: 'running' })
.autoResize();Step 4 — Add the backend group
const backendGroup = topology.createGroup('backend', {
label: 'Backend',
width: 600,
height: 200,
x: 100,
y: 340,
});
backendGroup
.addProcess({ id: 'api', label: 'API Server', state: 'running' })
.addProcess({ id: 'redis', label: 'Redis', shape: 'cylinder', state: 'running' })
.addProcess({ id: 'pg', label: 'PostgreSQL', shape: 'cylinder', state: 'running' })
.autoResize();The shape property accepts 'default' (rounded rectangle), 'cylinder' (database), 'diamond' (decision/gateway), 'cloud', and several others. See Node Types & Shapes.
Step 5 — Connect the nodes
topology.connect(source, target, options) creates a directed edge. The direction means source opens the TCP connection to target — it does not mean data only flows one way.
topology.connect('browser', 'api', { protocol: 'http', label: 'REST' });
topology.connect('api', 'redis', { protocol: 'redis', label: 'cache' });
topology.connect('api', 'pg', { protocol: 'postgresql', label: 'SQL' });Edge direction = who opens the socket. connect('api', 'pg') means the API server dials Postgres — not that data only flows from API to Postgres. Responses travel back along the same edge in reverse. Never add a second connect('pg', 'api') for responses.
Step 6 — Apply the topology
await topology.apply();This pushes all groups, nodes, and edges into the React Flow store. The canvas renders the diagram immediately.
Step 7 — Write the animation
After apply(), create a FlowBuilder and describe scenarios. Each scenario is an independent sequence of steps.
const flow = new FlowBuilder();Happy path scenario
flow.scenario('happy-path', 'Happy Path', 'Cache hit — fast response');
flow
.from('browser')
.showMessage('[GET] /api/products/7')
.to('api')
.showMessage('[AUTH] Token valid. Checking cache.')
.to('redis')
.showMessage('[HIT] Product 7 found in cache (TTL: 280s)')
.from('redis').to('api')
.showMessage('[CACHED] {id:7, name:"Widget Pro", price:49.99}')
.from('api').to('browser')
.showMessage('[200 OK] Response in 4ms');Cache miss scenario
flow.scenario('cache-miss', 'Cache Miss', 'Redis cold — falls back to Postgres');
flow
.from('browser')
.showMessage('[GET] /api/products/7')
.to('api')
.showMessage('[AUTH] Token valid. Checking cache.')
.to('redis')
.showMessage('[MISS] Key not found in cache')
.from('redis').to('api')
.showMessage('[NULL] Cache miss — querying database')
.from('api').to('pg')
.showMessage('[QUERY] SELECT * FROM products WHERE id = 7')
.from('pg').to('api')
.showMessage('[ROW] Product found. Writing to cache.')
.from('api').to('redis')
.showMessage('[SET] product:7 EX 300')
.from('api').to('browser')
.showMessage('[200 OK] Response in 38ms');Step 8 — Return the flow
The last line of every script must be return flow; (or return null; if there is no animation).
return flow;Complete script
Here is the full working script you can paste directly into the editor:
const topology = new TopologyBuilder(true);
// --- Topology ---
const clientGroup = topology.createGroup('clients', {
label: 'Clients',
width: 300,
height: 200,
x: 100,
y: 60,
});
clientGroup
.addProcess({ id: 'browser', label: 'Browser', state: 'running' })
.autoResize();
const backendGroup = topology.createGroup('backend', {
label: 'Backend',
width: 600,
height: 200,
x: 100,
y: 340,
});
backendGroup
.addProcess({ id: 'api', label: 'API Server', state: 'running' })
.addProcess({ id: 'redis', label: 'Redis', shape: 'cylinder', state: 'running' })
.addProcess({ id: 'pg', label: 'PostgreSQL', shape: 'cylinder', state: 'running' })
.autoResize();
topology.connect('browser', 'api', { protocol: 'http', label: 'REST' });
topology.connect('api', 'redis', { protocol: 'redis', label: 'cache' });
topology.connect('api', 'pg', { protocol: 'postgresql', label: 'SQL' });
await topology.apply();
// --- Animation ---
const flow = new FlowBuilder();
flow.scenario('happy-path', 'Happy Path', 'Cache hit — fast response');
flow
.from('browser')
.showMessage('[GET] /api/products/7')
.to('api')
.showMessage('[AUTH] Token valid. Checking cache.')
.to('redis')
.showMessage('[HIT] Product 7 found in cache (TTL: 280s)')
.from('redis').to('api')
.showMessage('[CACHED] {id:7, name:"Widget Pro", price:49.99}')
.from('api').to('browser')
.showMessage('[200 OK] Response in 4ms');
flow.scenario('cache-miss', 'Cache Miss', 'Redis cold — falls back to Postgres');
flow
.from('browser')
.showMessage('[GET] /api/products/7')
.to('api')
.showMessage('[AUTH] Token valid. Checking cache.')
.to('redis')
.showMessage('[MISS] Key not found in cache')
.from('redis').to('api')
.showMessage('[NULL] Cache miss — querying database')
.from('api').to('pg')
.showMessage('[QUERY] SELECT * FROM products WHERE id = 7')
.from('pg').to('api')
.showMessage('[ROW] Product found. Writing to cache.')
.from('api').to('redis')
.showMessage('[SET] product:7 EX 300')
.from('api').to('browser')
.showMessage('[200 OK] Response in 38ms');
return flow;What to try next
- Add a third scenario that simulates a Postgres timeout and a 503 response using
showError - Split the backend group into two groups:
cacheanddatabase - Add a layer to each node and use layer pills to toggle views
Continue to Core Concepts to understand how the topology, edge directions, and animation phases fit together.