Add js function available on every function node

Hi all,
as wrote in subject: there's a way (better if not inside flows but in settings or in a more "low level" way) to add fucntions (or prototype extension) so that these functions are available everywhere in every "functions" node?

For example: a function that extracts only numeric characters from a string or some simple utility.

Thank you in advance.

If you must go down this (some would say "anti-pattern") path, then you can already make your own library of functions, store them in global or flow context, then you can access them in any function node you desire.

The more pattern way is to break your solutions down into small nodes, pass it down the wire, update the msg, pass it on.

So the graphical way of doing what you ask is to use the link-call node to mass the msg through a re-useable flow (that can also contain utility functions) that then passes back the msg with any updates applied.

1 Like

Hi Steve,
Thank you for your time and for the answer.

Yes, that's absolutely true. But the scenario behind this question is really complicate to explain. I know the possibility to create functions inside global environment yet, but imho that's really weird. I'd prefer a solution in which I can extend the js with some simple functions focused on what I had to do (or better:focused on what the user had to do). Just lime the startup way on function node to load external libraries. That should be a good compromised.

It is possible to do this with a custom node or (probably, not tested it) with a plugin. It isn't that hard to expose some functions into the function node VM's.

HOWEVER, that comes with a massive caveat. The method is not documented, might change, and you might one day find yourself with a name clash.

Use a Immediately Invoked Function Expressions , IIFE

This is a trivial example. Somewhere in the Forum there is an explanation of how to create a *.js file and load it into Node-RED.

const utility = (function () {

    /*
    *   Functions Available
    *
    *       isNumber,
    *
    */

 
    /**
    * Description   Determines whether or not supplied argument is a number.
    *
    * @param  {*}       value     - The argument to check.
    *
    * @return {boolean}           - True if the argument is a number, otherwise false.
    */
    function isNumber(value) {
        return typeof value === 'number' && isFinite(value)

    } // End Function isNumber()

    return {

        isNumber,


    };

}())

global.set('utility', utility, 'file')
1 Like

I'm really close to the solution.
@Buckskin I see that possibility:

https://flows.nodered.org/flow/195773d3b493d81c9bf012f64da02ea3

And I create inside /data a file with your example (without the global.set).

Then inside setting.js:

    functionGlobalContext: {
	      utility:require('./ysutils.js')
        // os:require('os'),
    },

Restarting nodered, everything works except that if I call inside a function node my function:

var utility = global.get('utility');
node.warn(utility);
return msg;

It returns "empty".

Uhm... why?

Hard to say without knowing what the required module looks like. It is exporting something I assume?

const utility = (function () {

    /*
    *   Functions Available
    *
    *       isNumber,
    *
    */


    /**
    * Description   Determines whether or not supplied argument is a number.
    *
    * @param  {*}       value     - The argument to check.
    *
    * @return {boolean}           - True if the argument is a number, otherwise false.
    */
    function isNumber(value) {
        return typeof value === 'number' && isFinite(value)

    } // End Function isNumber()

    return {

        isNumber,


    };

}())

// global.set('utility', utility, 'file')

// module.export (utility);



This is the content of the file ysutility.js, inside /data.

I tried with "export" but with no chance

No, that is not a valid Node.js module and therefore the require will never return anything useful.

It should be something like:

module.exports {
    /**
    * Description   Determines whether or not supplied argument is a number.
    * @param  {*}       value     - The argument to check.
    * @return {boolean}           - True if the argument is a number, otherwise false.
    */
    function isNumber(value) {
        return typeof value === 'number' && isFinite(value)
    } // End Function isNumber()
}

In your function node, it would look like

const {isNumber} = global.get('utility')
// ...
2 Likes

Thank you,
I just arrived at the same solution :smiley:

Thank you @TotallyInformation !!!

You unlocked my brain :smiley: :smiley: !

EDIT: just for completion, something like:

function isNumber(value) {
  return typeof value === 'number' && isFinite(value);
}

function isString(value) {
  return typeof value === 'string';
}

module.exports = {
  isNumber,
  isString
};

in my .js file, then inside settings.js:

    functionGlobalContext: {
	      utility:require('./ysutils.js')
        // os:require('os'),
    },

After restarting NR, I'm able to call functions inside my function node:

const utility = global.get('utility');
node.warn(utility);
return msg;

returns in debug view:

 object==>
          isNumer:function
          isString:function

Thank you - both of you. I had this in operation once but in the intervening years I had forgotten about it. (old age creeping in :grin:)

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.