Sharing a single instance

#1

Hi folks,

I'm redesigning my node-red-contrib-http-logger to use the mitm library for intercepting http(s) traffic. However after debugging a few evenings, it became at the end clear that I'm only allowed to create 1 mitm instance:

var Mitm = require("mitm");
var mitm = new Mitm();

A user should be able to add multiple logger nodes to his flow (node1 to listen for requests to host1, node2 to listen for requests to host2, ....). But they all have to share this single instance.

In Java I would simply create a singleton, but not sure to do it in Javascript for Node-RED. Can anybody share please a code snippet how to accomplish this?

Thanks !
Bart

#2

maaybe there are better ways, but you could store this object in the global context under a very unique name. If the object does not exist, then you would create it.

#3

each node has its own context... no need to pollute the global one.

#4

according to Barts comment

one can have only one mitm instance in the node process

#5

Anything that you put outside the function you include in RED.nodes.registerType() (param 2) is common to all instances of your node.

If you need multiple instances, they have to go inside that function. You also have to remember to destroy things if they are set up inside that function using node.on('close', ...) otherwise you will probably end up with memory leaks and other undesirable side effects).

In uibuilder for example, I use this to create a single logging instance for all instances of the node. v2 also uses this to improve efficiency by adding ExpressJS middleware routes for loaded libraries only once for all instances. Similarly, API's are only set up once no matter how many instances of uibuilder you have in your flows.

1 Like
#6

@cinhcet, @dceejay,
Thanks but I thought using (node/flow/global) contexts wouldn't be a good solution in this case. Suppose I would do it like this:

var mitm = new Mitm();
this.context().global.set(mitm);

Inside that constructor the whole interception stuff is being setup. So what happens if a Node-RED contextstore is storing and loading that instance? Therefore I thought it would be better to avoid using the context???

@TotallyInformation: Suppose I do it like this:

module.exports = function(RED) {
    var Mitm = require("mitm");
    
    var mitm = new Mitm();

    function HttpLoggerNode(config) {
        RED.nodes.createNode(this, config);

   }

    RED.nodes.registerType("http-logger",HttpLoggerNode);
}

Do you mean that the "mitm" variable is only initialized once (i.e. constructor is only executed once), and available to all the HttpLoggerNode's in my flow?

1 Like
Best practice for sharing static object between instances of a node type
#7

Correct. The function your module exports gets called once when Node-RED starts up and loads you module. So the mitm function will only be called once and will be in-scope for all code with the function(RED) { }.

If you are able to detect if mitm has been initialised or not, you might want to add some error handling and reporting just in case some other module uses mitm and gets there first.

2 Likes
#8

:slight_smile:

Really, you should do the require outside the exports since the Node-RED service doesn't need access to it directly. Indeed, I think that var mitm ... can be outside the exports as well. Though I would make that a const mitm ... myself. Same for the require statement. Putting things outside the exports should mean that they can't be accidentally accessed by anything else in Node-RED other than your node type itself. Might not make any difference really given how Node-RED works but good defensive programming style anyway.

1 Like