Function callback not firing on second machine

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:

  1. Each child node sends a registration request to the parent using node.id at startup using 1 wire.
  2. The parent node dynamically retrieves and registers child nodes using RED.nodes.getNode(childId).
  3. 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: