Function variables vs custom properties in 'node' objects

Hi experts,
In my custom-developed nodes, I maintain & work with local (per node instance) properties.
What would be the difference (and better way of doing this):
Option 1: as function variables

module.exports = function(RED)
{
    function myFunction(config)
	{
		RED.nodes.createNode(this,config);
		const node = this;
		var myProperty = 123;
		var myProperty2 = "abc";
...

Option 2: as node properties

module.exports = function(RED)
{
    function myFunction(config)
	{
		RED.nodes.createNode(this,config);
		const node = this;
		node.myProperty = 123;
		node.myProperty2 = "abc";
...

Thanks!

IMO, for the sake of maintaining readability.

I would add them to the node const.
This way, you know that these properties are global to the inner workings of your node instances

This is however, my preferred approach - neither is right or wrong.

module.exports = function(RED) {

    function myFunction(config) {

        RED.nodes.createNode(this, config);
        const node = this;
        node.myProperty = 123;
        node.myProperty2 = "abc";

        const someOtherFunction = () => {
            // do something with node.myProperty
        }
    }
}

If these are more configuration values (that can change via the HTML file) as opposed to internal states, then you should be using the node properties API

Node properties : Node-RED (nodered.org)

Thanks @marcus-j-davies !
That's my preference as well, enabling me to avoid scope issues and be able to pass less arguments in my inner workings.
My concern is whether by modifying a framework object (node) I will not be stepping on anyone's toes, or that some framework code may impact my properties...

I don't believe it to be a concern.

createNode adds the property credentials to your object so maybe don't mess with that or id (or anything beginning with _ )

I and others attach internal state objects all the time, as long as they don't interfere with anything that is added by the runtime - i cant see it being a problem.

1 Like

The node instance is isolated - there is no overlap elsewhere unless you deliberately target global.

However, as your (if your) code gets larger and more complex, you may wish to refactor it into other modules. At that point, you will be glad you added the variables as properties on the node instance's this object (which most people interpret as node) since you can simple pass this as a reference to your module functions/methods.

If you want to see that in action, have a look at the code for uibuilder. In fact, these kinds of confusion are the reason I refactored my nodes different to the "standard" way. node-red-contrib-uibuilder/uibuilder.js at v6.1.0 · TotallyInformation/node-red-contrib-uibuilder (github.com)

You will see that the export is on a separate line so that it gets a named function. Then the exported function calls a setup function and registerType, nothing else. The registerType includes nodeInstance as its callback so that you don't get confused by which bits are part of the overall module and which are specific to the instance. nodeInstance does the usual things except that it does not create the unnessary node variable. However, wherever this is passed to another function/method, the receiving function always calls it node.

The other thing of note is that I create a module-level (think of that as the "local global" :slight_smile: ) object to hold data for the module as a whole but that may still need to be passed to other includes. Things like the name of the node for example.

The numbered comments in the code show what order things happen in so there is no confusion.

This approach really helped me get to grips with the order of things and with the isolation/availability of variables.

1 Like

Thank you @marcus-j-davies and @TotallyInformation !
I'll follow your advice.

Well, turns out there is another option - setting custom properties in the node instance, without changing the node object, while avoiding any variable scope issues:

module.exports = function(RED)
{
    function myFunction(config)
	{
		RED.nodes.createNode(this,config);
		const node = this;
		(node.context()).set("myProperty",123);
		(node.context()).set("myProperty2","abc");
...

But that isn't the same at all. You are not setting values on the node instance but actually in the Node-RED context store (albeit in the area dedicated to that node). That context store could be set up differently on different Node-RED instances. So it might, for example, be set for persistent storage. While that might be what you want, it may well not be.

So you are not avoiding scope issues, you are merely swapping one set of considerations for another.

Oh, and of course, the context store can be examined by anyone with Editor access. Again that might be helpful or it might not be.

OK, noted.
I thought the context was a local node space, not a designated area in a global space.
Thanks for pointing this out!