How to Modularize and Reuse Function Node Code in Node-RED?

  • I have a Node-RED flow where several function nodes contain more than 100 lines of JavaScript code each. Managing and writing unit tests for such large code blocks has become increasingly difficult.
  • I’m looking for a way to modularize or separate the function node code .
  1. Store the JavaScript code in external files for better maintainability.
  2. Reuse the same code across multiple function nodes wherever required.
  3. Easily write and run unit tests for the extracted code.

I have two ideas:

  1. Create a Separate Node for Each Function
  • "I have created a custom lower-case node to check if it's possible to create a separate node for each functionality instead of using function nodes and linking them together. Is this approach feasible in Node-RED?".
  1. Create External Modules and Import Them
  • Write the functions in external JavaScript modules, then import and use them in Node-RED function nodes.

Which approach is better? or any other method have?

In the past I've used one of the following approaches:

1. Put the function into a subflow

Advantages:

  • proper error handling using the catch node
  • make use of node status
  • export/import subflows for reuse in other Node-RED instances

Disadvantage:

  • you might end up with many subflows cluttering the left palette bar

2. Use link-calls

Advantages:

  • better organization
    • put your shared functions on a separate flow tabs --> you can hide and/or lock flow tabs!
    • no cluttering of the palette bar

Disadvantage:

  • error handling is more complicated
    • errors can't be caught by targeting the link-call node because you can't pass them to the link result node
    • you'd have to handle errors manually on each call side by evaluation for an error property... too much boilerplate for me :see_no_evil:
  • no node status on the calling side

There was a discussing regarding the error handling with link-calls, but no conclusion has been reached: Capturing errors caused inside link-call flows - #5 by kuema

That's why I prefer the subflow solution, because I like clean error handling using "try-catch" :nerd_face:

1 Like

@kuema

  • I have more than 100 lines of code inside a Function Node and want to handle that logic outside of the node for unit testing or modularity, i can move the logic into an external JavaScript file (module) or Create a Separate Node for Each Function .
  • This approach makes it easier to test and maintain.Which approach is better? or any other way?

Both are valid approaches. Depends on how many different custom nodes you'd need to create.

So the first solution might be easier.
You can use the module import of the function node to include your custom code.

Keep in mind that you need to restart Node-RED after making changes for both solutions. You wouldn't need to restart if using the subflow or link-call approach.

1 Like
  • Currently, i am using a more than 10 function node and it has more than 100 code; it's hard to write unit test case
  • Still, i am confucion
  • I can use the External Modules and Import of the function node to include my custom code. or follow this method Creating your first node : Node-RED

I followed this link and i did.
Creating your first node : Node-RED
so I want to crate more custom node like lowe-case node in left side panel, right?

Now try running the function node code outside of Node-RED

is this good method for production?

Depending on your needs ofcourse; you can create a npm module which doesnt necessarily have to become a node-red node, but it becomes importable for a function node (see the setup tab within a function node).

You could also expose your functions are flow/global context objects and call them when needed.
Personally I like link-call a lot, but as kuema indicated, debugging is more complicated.

Subflows are also an option, but keep in mind that each subflow node becomes an instance (ie, not efficient).

A module is certainly the way to go for this. Alternatively, you can create a plugin instead of a full node. In that case, you could even attach your code to the RED object that gets exposed to function nodes.

IMHO, don't even think about subflows (they introduce many issues). What I do to modularize my code is pack my parameters in the msg and use link calls to call other function nodes as if they were procedures in my code. Note however that calling an external node is an async operation, so beware of race conditions.

Can you explain what you mean by an external node please?

For functions that I use a lot like date/time formatting, I have a "master" function note that is called at start up and creates a global variable.

If i then want to use that function in my other nodes, I use call it

const func = global.get("functions")

then do something like:

func.formatDate( new Date() );

I'm just referring to other function nodes, i.e. I extract reusable pieces of code from my function node into independent standalone function nodes, serving as general-purpose modules which I can call from multiple places with call-links.

In that case they will not be asynchronous any more than an inline function node is asynchronous.

As functions in stored in global context , when called without async/await (or promise or callback) they will be synchronous.

Functions called by link-call will be async in relation to other flow nodes (but for all intents and purposes when in a single (in series) flow) they will appear to be synchronous from first node input to last node output.

In other words, splitting functions out to reusable subroutines that are graphically called via link-call is in line with the asynchronous advantages of node js and the visual aspect of flow based programming.

2 Likes