Hey @TotallyInformation, => Cleaned up my usual scribles a bit so others might benefit from it.
Your insights led me to a clean and effective solution for dynamically linking parent and child nodes in Node-RED without cluttering the UI with excess wires.
The Problem (bug) : Node-RED Clones Message Payloads
I discovered that passing objects through msg.payload
can introduce inconsistencies due to Node-RED’s internal cloning behavior. My EventEmitter
was being altered unexpectedly when sent through a message.
Instead of relying on msg.payload
for passing object references, I realized I could leverage Node-RED's built-in node registry (RED.nodes.getNode(id)
) to retrieve the correct parent instance dynamically.
The Solution: Hybrid Parent-Child Registration
To avoid excessive wires while keeping my architecture modular, I implemented a hybrid approach where:
- Each child node sends a registration request to the parent using
node.id
at startup using 1 wire. - The parent node dynamically retrieves and registers child nodes using
RED.nodes.getNode(childId)
. - The child-parent relationship is automatically managed—users don't need to worry about connecting every child node manually.
Step 1: Child Nodes Register Themselves at Startup
Each child node sends its ID to its parent node via a message at startup.
setTimeout(() => {
let msgs = [];
// Notify parent nodes of this child node's existence
msgs[2] = { topic: "registerChild", payload: node.id, positionVsParent: "upStream" };
msgs[3] = { topic: "registerChild", payload: node.id, positionVsParent: "downStream" };
// Send registration messages
this.send(msgs);
}, 100);
- The child only sends an ID, preventing object cloning issues.
- No need to manually wire every child to its parent—Node-RED will handle the routing.
- This ensures a clean UI with minimal wiring.
Step 2: Parent Nodes Dynamically Retrieve and Register Children
The parent node listens for registerChild
messages and registers the child using its ID.
node.on("input", function (msg, send, done) {
switch (msg.topic) {
case 'registerChild':
const childId = msg.payload;
const childObj = RED.nodes.getNode(childId); // Retrieve child node instance
// Register the child in the correct position
m.childRegistrationUtils.registerChild(childObj.source, msg.positionVsParent);
break;
}
});
- The parent dynamically retrieves the correct child instance from Node-RED’s registry.
- No need to pass complex object references through messages.
- The architecture remains modular and easy to maintain.
Why This Approach Is Ideal for me
Avoids Cloning Issues
- Using
RED.nodes.getNode(id)
ensures we always reference the actual node, not a modified clone.
Removes UI Clutter
- No unnecessary wires—users only connect essential process flows.
Ensures Automatic Parent-Child Registration
- Children automatically find and register with their parents at startup.
Maintains Standard Node-RED Behavior
- The architecture still supports standard wired outputs for debugging or additional logic.
Gives me a process architecture
- I wanted a process like way to visualize the nodes and leverage my OOP style library.
Thanks again for the inspiration, @TotallyInformation! Hope this helps others looking for a different way to structure their Node-RED flows.
Here is an example using downstream assets: