Overview
The @statelyai/graph library uses plain JSON-serializable objects to represent graphs. There are no classes required—you can create graphs using simple object literals or factory functions.
Basic Structure
A graph consists of three main components:
Nodes Vertices in the graph with an ID and optional data
Edges Connections between nodes with source and target
Graph Container with metadata and configuration
Graph Type
The Graph interface defines the core structure:
interface Graph < TNodeData = any , TEdgeData = any , TGraphData = any > {
id : string ;
type : 'directed' | 'undirected' ;
initialNodeId : string | null ;
nodes : GraphNode < TNodeData >[];
edges : GraphEdge < TEdgeData >[];
data : TGraphData ;
direction ?: 'up' | 'down' | 'left' | 'right' ;
style ?: Record < string , string | number >;
}
Properties
id — Unique identifier for the graph
type — Either 'directed' or 'undirected'
initialNodeId — Starting node for traversals (nullable)
nodes — Array of graph nodes
edges — Array of graph edges
data — Custom metadata attached to the graph
direction — Layout direction hint for rendering
style — Custom styles for visual rendering
Node Type
Nodes represent vertices in the graph:
interface GraphNode < TNodeData = any > {
type : 'node' ;
id : string ;
parentId ?: string | null ;
initialNodeId ?: string | null ;
label : string ;
data : TNodeData ;
// Optional visual properties
x ?: number ;
y ?: number ;
width ?: number ;
height ?: number ;
shape ?: string ;
color ?: string ;
style ?: Record < string , string | number >;
}
Properties
type — Always 'node' for type discrimination
id — Unique identifier (required, non-empty string)
parentId — Parent node ID for hierarchical graphs (see Hierarchical Graphs )
initialNodeId — Starting child node for compound nodes
label — Display label (defaults to empty string)
data — Custom data payload
Visual properties — Optional position, size, shape, color, and styles
Edge Type
Edges represent connections between nodes:
interface GraphEdge < TEdgeData = any > {
type : 'edge' ;
id : string ;
sourceId : string ;
targetId : string ;
label : string ;
data : TEdgeData ;
// Optional visual properties
x ?: number ;
y ?: number ;
width ?: number ;
height ?: number ;
color ?: string ;
style ?: Record < string , string | number >;
}
Properties
type — Always 'edge' for type discrimination
id — Unique identifier (required, non-empty string)
sourceId — ID of the source node
targetId — ID of the target node
label — Display label (defaults to empty string)
data — Custom data payload
Visual properties — Optional position, size, color, and styles
Creating Graphs
Using the Factory Function
The recommended way to create graphs is with createGraph():
import { createGraph } from '@statelyai/graph' ;
const graph = createGraph ({
id: 'my-graph' ,
type: 'directed' ,
nodes: [
{ id: 'a' , label: 'Start' },
{ id: 'b' , label: 'Middle' },
{ id: 'c' , label: 'End' }
],
edges: [
{ id: 'e1' , sourceId: 'a' , targetId: 'b' , label: 'next' },
{ id: 'e2' , sourceId: 'b' , targetId: 'c' , label: 'next' }
]
});
Using Plain Objects
Since graphs are plain objects, you can create them with literals:
import type { Graph } from '@statelyai/graph' ;
const graph : Graph = {
id: 'manual' ,
type: 'directed' ,
initialNodeId: null ,
nodes: [
{
type: 'node' ,
id: 'a' ,
label: 'Node A' ,
data: undefined
}
],
edges: [],
data: undefined
};
Using createGraph() is recommended because it handles default values automatically. For example, label defaults to '', type defaults to 'directed', and visual properties are only included if provided.
Config vs. Primary Types
The library provides two sets of types:
Lenient types for creating graphs—most fields are optional:
interface GraphConfig < TNodeData , TEdgeData , TGraphData > {
id ?: string ;
type ?: 'directed' | 'undirected' ;
nodes ?: NodeConfig < TNodeData >[];
edges ?: EdgeConfig < TEdgeData >[];
// ...
}
interface NodeConfig < TNodeData > {
id : string ; // Required
label ?: string ;
data ?: TNodeData ;
// ...
}
interface EdgeConfig < TEdgeData > {
id : string ; // Required
sourceId : string ; // Required
targetId : string ; // Required
label ?: string ;
data ?: TEdgeData ;
// ...
}
Primary Types (Output)
Strict types with all fields resolved:
interface Graph < TNodeData , TEdgeData , TGraphData > {
id : string ; // Always present
type : 'directed' | 'undirected' ; // Always present
nodes : GraphNode < TNodeData >[]; // Always array
edges : GraphEdge < TEdgeData >[]; // Always array
// ...
}
TypeScript Generics
All graph types support three generic parameters:
Graph < TNodeData , TEdgeData , TGraphData >
TNodeData — Type of node.data
TEdgeData — Type of edge.data
TGraphData — Type of graph.data
Example with Custom Data
interface NodeData {
value : number ;
visited : boolean ;
}
interface EdgeData {
weight : number ;
}
const graph = createGraph < NodeData , EdgeData >({
nodes: [
{ id: 'a' , data: { value: 1 , visited: false } },
{ id: 'b' , data: { value: 2 , visited: false } }
],
edges: [
{ id: 'e1' , sourceId: 'a' , targetId: 'b' , data: { weight: 5 } }
]
});
// TypeScript knows the data types
const nodeValue : number = graph . nodes [ 0 ]. data . value ;
const edgeWeight : number = graph . edges [ 0 ]. data . weight ;
Graph Wrapper Class
For an object-oriented API, use GraphInstance:
import { GraphInstance } from '@statelyai/graph' ;
const instance = new GraphInstance ({
nodes: [{ id: 'a' }, { id: 'b' }]
});
instance . addNode ({ id: 'c' });
instance . addEdge ({ id: 'e1' , sourceId: 'a' , targetId: 'c' });
console . log ( instance . nodes . length ); // 3
console . log ( instance . edges . length ); // 1
// Get the plain graph object
const plainGraph = instance . toJSON ();
GraphInstance is just a wrapper around a plain Graph object. All methods delegate to standalone functions. The underlying graph is accessible via instance.graph.
Directed vs. Undirected Graphs
Directed Graphs
Edges have a clear direction from source to target:
const directed = createGraph ({
type: 'directed' ,
nodes: [{ id: 'a' }, { id: 'b' }],
edges: [{ id: 'e1' , sourceId: 'a' , targetId: 'b' }]
});
// a → b
// hasPath(directed, 'a', 'b') === true
// hasPath(directed, 'b', 'a') === false
Undirected Graphs
Edges can be traversed in both directions:
const undirected = createGraph ({
type: 'undirected' ,
nodes: [{ id: 'a' }, { id: 'b' }],
edges: [{ id: 'e1' , sourceId: 'a' , targetId: 'b' }]
});
// a — b
// hasPath(undirected, 'a', 'b') === true
// hasPath(undirected, 'b', 'a') === true
Examples
Simple Graph
const graph = createGraph ({
nodes: [
{ id: 'start' , label: 'Start' },
{ id: 'end' , label: 'End' }
],
edges: [
{ id: 'transition' , sourceId: 'start' , targetId: 'end' }
]
});
Weighted Graph
interface WeightedEdge {
weight : number ;
}
const graph = createGraph < any , WeightedEdge >({
nodes: [
{ id: 'a' },
{ id: 'b' },
{ id: 'c' }
],
edges: [
{ id: 'e1' , sourceId: 'a' , targetId: 'b' , data: { weight: 10 } },
{ id: 'e2' , sourceId: 'b' , targetId: 'c' , data: { weight: 20 } },
{ id: 'e3' , sourceId: 'a' , targetId: 'c' , data: { weight: 5 } }
]
});
interface GraphMetadata {
name : string ;
version : string ;
createdAt : Date ;
}
const graph = createGraph < any , any , GraphMetadata >({
id: 'workflow' ,
data: {
name: 'User Onboarding' ,
version: '1.0.0' ,
createdAt: new Date ()
},
nodes: [{ id: 'welcome' }, { id: 'setup' }],
edges: [{ id: 'e1' , sourceId: 'welcome' , targetId: 'setup' }]
});
Next Steps
Hierarchical Graphs Learn about parent-child relationships and compound nodes
Visual Graphs Add position and size for rendering
Serialization Understand JSON serialization and plain objects
Operations Explore graph operations (add, update, delete)