Skip to content

Building a Graph

This guide covers the day-to-day work of building and maintaining a graph: how to load data, add and remove elements, query them with predicates, update their attributes and data, iterate collections, and inspect connected components.

For the underlying model (what an attribute is versus your own data), see The Graph Model. For the full method list, see API: Graph.

Setting up

Every example below starts from a Nodus instance. With a container you get a rendered graph; without one you run headless (useful for tests and data prep).

import { Nodus } from '@kortexya/nodus';
const nodus = new Nodus({ container: document.getElementById('graph') });

Loading a graph: setGraph vs addGraph vs clearGraph

Nodus accepts plain JSON. A graph is { nodes, edges }; each node may carry an id, visual attributes, and your own data; each edge additionally needs a source and target.

// Replace the entire graph (clears anything already loaded).
await nodus.setGraph({
nodes: [
{ id: 'a', attributes: { x: 0, y: 0 } },
{ id: 'b', attributes: { x: 100, y: 0 } },
],
edges: [
{ source: 'a', target: 'b' },
],
});

Use addGraph to merge more data into what’s already there, rather than replacing it:

// Merge: keeps a and b, adds c and a new edge.
await nodus.addGraph({
nodes: [{ id: 'c', attributes: { x: 50, y: 80 } }],
edges: [{ source: 'b', target: 'c' }],
});

And clearGraph removes everything:

nodus.clearGraph();

Try it live — this demo builds a RawGraph from plain records and loads it:

Build from your data Open in new tab ↗

A typed example

The constructor takes two generics: ND for node data and ED for edge data. Typing them once gives you autocomplete and type-checking everywhere you read or write data.

import { Nodus } from '@kortexya/nodus';
interface Person { name: string; team: string; }
interface Rel { kind: 'reports-to' | 'collaborates'; since: number; }
const nodus = new Nodus<Person, Rel>({
container: document.getElementById('graph'),
});
await nodus.setGraph({
nodes: [
{ id: 'ada', data: { name: 'Ada', team: 'core' } },
{ id: 'borg', data: { name: 'Borg', team: 'core' } },
],
edges: [
{ source: 'ada', target: 'borg', data: { kind: 'collaborates', since: 2021 } },
],
});

Now node.getData() is typed as Person and edge.getData() as Rel.

Adding elements

Add a single element with addNode / addEdge, or many at once with addNodes / addEdges. Each call returns the created element (or a list).

// One node — returns the created Node.
const carol = nodus.addNode({ id: 'carol', data: { name: 'Carol', team: 'design' } });
// Many nodes — returns a NodeList.
const newcomers = nodus.addNodes([
{ id: 'dre', data: { name: 'Dre', team: 'design' } },
{ id: 'efi', data: { name: 'Efi', team: 'ops' } },
]);
// One edge.
const link = nodus.addEdge({
source: 'ada',
target: 'carol',
data: { kind: 'collaborates', since: 2023 },
});
// Many edges.
nodus.addEdges([
{ source: 'carol', target: 'dre', data: { kind: 'collaborates', since: 2022 } },
{ source: 'dre', target: 'efi', data: { kind: 'reports-to', since: 2024 } },
]);

If you omit an id, Nodus assigns one. Visual attributes and your data are both optional on every element.

Removing elements

The remove methods return Promise<void>await them if you need to know the removal has completed (for example before re-running a layout).

// By id or by element reference.
await nodus.removeNode('efi');
await nodus.removeEdge(link);
// In bulk — pass ids, elements, or a NodeList/EdgeList.
await nodus.removeNodes(['dre', 'carol']);
await nodus.removeEdges(nodus.getEdges());

Removing a node also removes the edges attached to it.

Try it live — use the controls to add and remove nodes and edges on a running graph:

Add & remove nodes live Open in new tab ↗

Querying with getNode / getNodes and predicates

getNode(id) / getEdge(id) fetch a single element. getNodes() / getEdges() return the whole collection, or — given a predicate function — just the matching elements.

// Single element by id.
const ada = nodus.getNode('ada');
// All nodes.
const everyone = nodus.getNodes();
// Filtered: only the design team. Returns a NodeList.
const designers = nodus.getNodes().filter((node) => node.getData('team') === 'design');
// Filtered edges: collaborations only.
const collabs = nodus.getEdges().filter((edge) => edge.getData('kind') === 'collaborates');

getData(path?) reads a single field when given a path ('team') or the whole object when called with no argument.

Updating elements

Change visual attributes with setAttributes (or setAttribute for one), and change your payload with setData.

// Visual change — paint the core team.
ada.setAttributes({ color: '#4f46e5', radius: 14 });
// Update one attribute.
borg.setAttribute('radius', 18);
// Update your data. Pass a path + value...
ada.setData('team', 'platform');
// ...or merge a partial object.
carol.setData({ name: 'Carol R.' });

Collections broadcast the same calls to every member, so you can style a whole filtered set in one line:

// Highlight every designer at once.
designers.setAttributes({ outerStroke: { color: '#10b981', width: 3 } });

See Styling Nodes & Edges for the full set of visual attributes.

Iterating a NodeList

A NodeList (and EdgeList) is array-like and iterable. You can loop it directly, or use the functional helpers — filter, map, find, reduce, sort, and friends. filter returns another NodeList, so queries compose.

const nodes = nodus.getNodes();
// Direct iteration.
for (const node of nodes) {
console.log(node.getId(), node.getData('name'));
}
// Size and indexed access.
console.log('total nodes:', nodes.size);
const first = nodes.get(0);
// Functional pipeline: design-team names, sorted.
const names = nodes
.filter((n) => n.getData('team') === 'design')
.map((n) => n.getData('name'))
.sort();
// Set operations compose two lists.
const others = nodes.subtract(designers);

Broadcast reads return arrays: nodes.getId(), nodes.getData('team'), and nodes.getPosition() each give you an array aligned with the list.

Connected components

getConnectedComponents() partitions the graph into its disconnected pieces — each component is returned as a NodeList. Use it to find islands, count clusters, or focus on the subgraph around a node.

// Every component in the graph.
const components = nodus.getConnectedComponents();
console.log('component count:', components.length);
for (const component of components) {
console.log('size:', component.size);
}
// Just the component containing a given node.
const adasGroup = nodus.getConnectedComponentByNode('ada');
adasGroup.setSelected(true);

You can also ask an individual node for its component via node.getConnectedComponent().

Next steps