Getting StartedYour First Script

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:

my-first-diagram.ts
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: cache and database
  • 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.