API ReferenceEdges & Protocols

Edges & Protocols

Edges represent the transport-layer connections between nodes. Understanding edge direction is the single most important concept for building correct cloud-arch diagrams.


The Edge Direction Rule

⚠️

connect(A, B) means A opens the TCP connection to B.

The arrow direction answers the question: who dials whom? It does NOT indicate the direction of data flow. Once a TCP connection is established, data flows in both directions. Responses travel back along the same edge in reverse animation — you never need a duplicate edge for responses.

Think of it as asking: which process runs connect() or dial() at the OS socket level?

  • Spark reads Postgres via JDBC → Spark calls connect() → connect('spark', 'pg')
  • A Kafka consumer reads from a broker → Consumer calls connect() → connect('flink', 'kafka')
  • Airflow submits a Spark job via the K8s API → Airflow calls connect() → connect('airflow', 'spark')

Correct vs wrong examples

ScenarioCorrectWrongReason
Spark reads Postgres via JDBCconnect('spark', 'pg')connect('pg', 'spark')Spark opens the JDBC socket to Postgres
Airflow submits Spark jobconnect('airflow', 'spark')connect('spark', 'airflow')Airflow calls the K8s/Spark API
Debezium reads WALconnect('debezium', 'pg-primary')connect('pg-primary', 'debezium')Debezium opens the replication slot
Kafka consumerconnect('flink', 'kafka')connect('kafka', 'flink')Consumer connects to the broker
Trino queries Icebergconnect('trino', 'iceberg')connect('iceberg', 'trino')Trino calls the Iceberg REST catalog API

Responses do not need new edges

The FlowBuilder handles reverse animation automatically:

// ONE edge in the topology
topology.connect('client', 'api', { protocol: 'http', label: 'REST' });
 
// Animation: request goes forward, response goes backward on the SAME edge
flow
  .from('client').to('api')   // forward
  .showMessage('[GET] /data')
  .from('api').to('client')   // REVERSE — no extra edge needed
  .showMessage('[200 OK] Here is your data');

Edge types

The type property in ConnectionOptions controls which React component renders the edge.

TypeComponentDescription
floatingAnimatedFloatingEdgeDefault. Smooth animated dashes. Works correctly with nodes inside groups.
animatedAnimatedEdgeSimple animated dashes. Use for basic diagrams without group nesting.
pulsePulseEdgePulsing glow effect. Recommended for Kafka, RabbitMQ, and event-stream connections.
topology.connect('producer', 'kafka', { type: 'pulse',    protocol: 'kafka' });
topology.connect('api',      'db',    { type: 'floating', protocol: 'postgresql' });

Protocol color map and line style

The protocol property controls the edge color and the dash pattern. Always set it explicitly on every connect() — the validator reports EDGE_WITHOUT_PROTOCOL as a warning when it is missing.

The line style rule. A solid line is a long-lived connection: a socket that stays open, a database session, a broker subscription, a replication stream. A dashed line is request/response over HTTP: the connection is opened for one exchange and closed. Anything not on the solid list — including unknown and semantic values like call, internal, syscall, diagnose — renders dashed.

ProtocolColorHexDash patternTypical use
httpGreen#4CAF50Dashed (request/response)REST APIs, HTTP webhooks
httpsBlue#2196F3Dashed (request/response)TLS-encrypted HTTP
grpcPurple#9C27B0Dashed (request/response)gRPC unary calls (grpc-stream is solid)
graphql——Dashed (request/response)GraphQL over HTTP
postgresqlDark blue#336791Solid (long-lived session)PostgreSQL / JDBC
mysqlSteel blue#4479A1Solid (long-lived session)MySQL
mongodbGreen#47A248Solid (long-lived session)MongoDB connections
redisRed#DC382DSolid (long-lived session)Redis commands
kafkaOrange#FF9800Solid (long-lived session)Kafka produce / consume (pulse edge)
rabbitmqOrange#FF9800Solid (long-lived session)AMQP messages (pulse edge)
websocketTeal#00BCD4Solid (long-lived session)WebSocket connections
tcpGray#607D8BSolid (long-lived session)Raw TCP
s3Green#569A31Dashed (request/response)Object storage (S3, MinIO) over HTTP

The full solid list lives in apps/web/src/constants/edgeSemantics.ts (SESSION_PROTOCOLS): tcp, websocket, wss, sse, sql, postgresql, postgres, mysql, mongodb, jdbc, clickhouse, cassandra, redis, resp, zookeeper, kafka, rabbitmq, amqp, pulsar, nats, replication, stream, grpc-stream.

If no protocol is specified, the edge renders solid as tcp (the type value is consulted first, then tcp). Do not rely on that: always set protocol explicitly so the line style carries meaning. A manual style: { strokeDasharray } on the edge overrides the semantic pattern.


Full edge options reference

topology.connect(source: string, target: string, options?: {
  label?: string;          // Text shown at the midpoint of the edge
  protocol?: string;       // Controls edge color
  type?: 'floating' | 'animated' | 'pulse';  // Edge component
  layer?: string;          // Visibility layer
  animated?: boolean;      // Enable/disable dash animation (default: true)
  bidirectional?: boolean; // Render arrowheads on both ends (default: false)
})

Common mistakes

Mistake 1: Adding reverse edges for responses

// WRONG — creates two visible edges on the canvas
topology.connect('client', 'api', { protocol: 'http' });
topology.connect('api', 'client', { protocol: 'http' }); // redundant
 
// CORRECT — one edge, FlowBuilder handles reverse automatically
topology.connect('client', 'api', { protocol: 'http' });

Mistake 2: Edge bypasses a proxy

// WRONG — client bypasses the circuit breaker in the response path
topology.connect('client', 'cb',      { protocol: 'http' });
topology.connect('cb',     'service', { protocol: 'http' });
topology.connect('service', 'client', { protocol: 'http' }); // bypasses cb!
 
// CORRECT — responses go back through the circuit breaker
topology.connect('client', 'cb',      { protocol: 'http' });
topology.connect('cb',     'service', { protocol: 'http' });
// Response path: service → cb (reverse) → client (reverse)

Mistake 3: Wrong initiator direction

// WRONG — a Kafka broker does not dial consumers
topology.connect('kafka', 'flink', { protocol: 'kafka' });
 
// CORRECT — Flink consumers dial the Kafka broker
topology.connect('flink', 'kafka', { protocol: 'kafka' });