How to use a core node inside of a custom node implementation

I stumbled upon NodeRed when I searched for an alternative to openHAB to control my 433MHz switches. openHAB ist too heavyweight for my use case. I detected NodeRed and created a NodeRed Flow with a dashboard in a couple of days and I like NodeRed now.

I used the HTTP-Request node in order to control my switches managed by pilight via the REST API which works fine. But I frankly would like to create a custom node for pilight in order to manage the pilight switches. For now I managed to create a config node and a pilight device node. As of now the node creates the URL to control the switches and a user has to connect the node with a HTTP-Request node in order to kick off the REST request. I now want to get the http request processing inside of my pilight node and a user of my node doesn't have to deal with the HTTP processing. I'm familiar with various programming languages but I'm new to JavaScript. What's the suggested way to initiate HTTP requests in a custom node implementation? Is there any way to use the existing HTTP-Request node implementation from inside my custom node implementation?

Just in case somebody is interested in my current custom node implementation : https://github.com/framps/node-red-contrib-pilight_switches

There is http request functionality in node.js itself but it can be a bit cumbersome. There is several nom modules which you could require in your custom ode which make it a bit easier. Here is a good overview on some ways to make a request from node:


You could of course also explore making a subflow out of it. Subflows can be very useful and have become quite powerful in providing many things that make them behave nearly like its own nodered node. You can also publish them to the flows library for others to use.
Edit:
You can also of course have a look at the code of the http nodes itself which you can find here:

Johannes

1 Like

if it is a simple http request without https, authentification blabla, then please do yourself a favor and use the "native" http module of nodejs. It isn't too complicated
No dependencies, a lot of tutorials and it will work in the future (for example, the very popular "request" module got deprecated)

Not sure I entirely agree with the other sentiments here.

I use both ExpressJS "apps" in uibuilder and it is very easy indeed since all of the setup work has been done for you. All you need is a reference to one of the apps - either the Editor one RED.httpAdmin if you only want to give access to administrators or the "user" one RED.httpNode for everything else.

He wants to make an http request, not create an http endpoint! (If you want to create an endpoint, then yes, use RED.httpNode)

1 Like

I would somewhat disagree on this. If the OP is not familiar with JavaScript some of the wildly popular HTTP request libraries will just be easier to use. Even if one of such was deprecated it doesn't mean it's the end of the world on a custom Node-RED node. We're not talking about a high value business system here if I understood correctly.

One of such library is got (also mentioned in @JGKK's link) by Sindre Sorhus, possibly the most active and dedicated Node.js library contributor. It was last updated 2 weeks ago. I would not hesitate a bit to use it. And if if it some day gets deprecated, so what? You can either replace it with another with a similar syntax or just continue using it.

It's not like it library will stop working if it gets deprecated. :slightly_smiling_face:

Thank you very much for all your replies. As I wrote I'm new to JS and you gave me a lot of links and advices I have to check now.

Just some additional information:

I started with request because that's what I found first when searching in the net. But I failed to get the require go through in my js implementation. So I spent some time and searched how I can solve this. I found I have to add something in settings.js in functionGlobalContext. I tried request:require('request') and other variations but failed all the time. Then I started to search for other alternatives but all the time I failed to get the required require in my code to work. I think it's just because I'm new to JS ...

Nevertheless I thought from a code reuse perspective it makes sense to reuse an existing node implementation in my custom node implementation. All replies give suggestions to use some JS external os JS internal implementation to handle a http request. Nobody replied how I can reuse the existing http request node. Looks like there is no way to get this done inside of NodeRed. Frankly I think that's a missing feature of NodeRed.

My implementation is not elegant but it works perfectly for me. It's not mission critical and so the suggestion to use request even it's no longer maintained sounds good to me. If I don't get the require issue solved I most probably will use the native JS http implementation. I only need to send a http request without any authorisation or other special http request features.

As I said my implementation is not very elegant. If possible I would like to create a custom node for the pilight switches and use them in my implementation.. I also plan to publish the custom node in the NodeRed library if I get it done so other nodeRed users can profit from my custom nodes.

Thats why i pointed you to subflows instead of custom nodes. You can use subflows like any other node. They offer a lot of flexibility to build a config ui to a certain level and you can just use the http node inside the subflow.

This is only true when you want to use external npm modules in function nodes within nodered. If you build a custom node you can use require as is in the js part of the node. You just have to remember to add any external npm modules you use in a custom node to the dependency part of the package json so that it automatically gets installed when your node is installed.

If you want to use an external module in a function node within nodered you have to add it to the functionGlobalContext like i did here for fuzzball for example:

functionGlobalContext: {
        fuzzball:require('fuzzball')
    },

and than you can use it in a function node within nodered like this:

var fuzzy = global.get('fuzzball');

while if you’d want to use the same in a custom node you would just add:

const fuzzball = require("fuzzball");

to the top of your js file and add:

"dependencies": {
    "fuzzball": "~1.3.0 "
    },

to the package json

1 Like

I'm sorry I missed to reply to your comment about subflows. It sounds promising and I will have a look into this approach. But how do I share a subflow with the community? If I have a custom-node it will be listed in the NodeRed library and can be searched for. Is there any infrastructure available where a subflow can be shared and searched for in NodeRed?

And thank you for your detailed explanation about the require stuff for functions and custom nodes. This makes something clearer for me I frankly mixed both approaches and that's most probably the root cause I failed with require the last days.

If you go to the flows tab on the website you can filter to show you only the flows people shared there. The flows category also includes all the subflows. You can add your subflow to the flows library there by logging in with your github account.
Edit:
Have a look at this subflow i made a while ago. Add it to your nodered and look inside it and play around with it. It should give you a good idea at some of the concepts and possibilities that subflows offer.

1 Like

That's great. I didn't notice the other tabs in the library. And your subflow sample is for sure a good starting point for me :+1: I will now continue first with the require issue and try to use request. I just want to understand how this works :slight_smile: and then investigate in a subworkflow . Actually this approach will require a user to import the pilight flow together with the subworkflow as far as I understand.

Important caveat here against it, and the reason why I moved my development away from got to axios even though I prefer got. got uses core NodeJS functionality which is actually pretty great. But sadly, one developer who is a former core nodejs dev made a package that monkey patches that same part of the core that got uses. As a result, urls starting with https are resolved as pointing to localhost. The specific package that did this is used (directly or indirectly) by over a million of other packages, including npm. The developer fixed his monkey patching by dropping that code and calling it a major breaking change: the offending package went from v4.0.0 with that issue, to a fixed v5.0.0. As a result, it’s not picked up as bugfix release and every depending package has to be told to update its dependency to version 4. This includes several other libraries by the same developer.

The result of this is that even if your contrib node doesn’t depend on any of these packages, your got based node can stop working if another node is added to your installation that does have this dependency. And while I love got, this pile of dung is not worth working around. I’ve stopped using got in any node-red related projects while happily using it elsewhere.

For further reading, see my addition to this issue: Migrate away from request package · Issue #2481 · node-red/node-red · GitHub

2 Likes

Is it not correct that If your package.json requires version 5 then even if another package requires version 4 yours will still get 5?

That is very interesting, didn't know that. To be honest I've only used got once in a hobby project as it was recommended back then by a "world class" JS developer I happen to know.

The JavaScript/Node.js culture is well known for it's stupidly fast changes but what can you do? Pretty much anything you do with the tech will be obsolete in 2-3 years. :slightly_smiling_face:

I don't know how that works normally, but if there's a version 4 present somewhere deep in your dependency tree (my node-red install has dependencies in nodes I use for some for version 4 and others for version 5), the core NodeJS code gets patched. I don't know how the conflict handling for installation works in dependencies, or that both are installed in some kind of dependency tree, but if a v4 is present anywhere you're screwed. There's no kinder way to say it. The offending library is agent-base, which is the core library used for many different proxy implementations, including the standard node-proxy-agent by the same developer (which according to github has 235k repositories depending on it). While agent-base has the offending code removed in v5, node-proxy-agent has a fixed dependency on version 4, and the developer has so far ignored all requests to update that dependency.
Just a selection of libraries using node-proxy-agent, either directly or as nested dependency: aws-js-sdk, node-auth0, npm, aws-cdk, nightwatch, and a few more. For example, if you use a node that uses nightwatch for e2e testing, and it was set up as dependency instead of dev-dependency you're already in trouble. Another library from the same developer is https-proxy-agent, which Node-RED has a direct dependency on. In that library the fixed dependency got pushed, again through a major release (3.0.1 to 4.0.0). Node-RED nowadays depends on https-proxy-agent@5.0.0, but since version 4.0.0 only got released in December last year it means that there's likely a large number of Node-RED versions that are by default having this issue.

I looked it up in the package.json history: a Node-RED version older than 1.0.4 will have an agent-base@4.0.0 nested-dependency with the monkey patched core NodeJS code. That means that if there's the possibility that your node is used on Node-RED versions 1.0.3 and below, you should not use got. Even on a completely clean/clear install, you'll run into this issue. The fact that I tested my code on a clean 1.0.4 meant I didn't run into it directly, it appears.
See https://github.com/node-red/node-red/issues/2469 and https://github.com/node-red/node-red/pull/2470

1 Like

Oh yes, normally one can have different versions of a module installed for different nodes, but since this one hacks the core it doesn't matter why it is installed, it just has to be there to cause the problem.

1 Like

and exactly this is the reason why I recommend to use the http module that is built in :slight_smile:
And if you compare the solutions JGKK has posted, the http one is not really more complicated then the others. I would even argue that it is easier for people new to javascript, since it does not use promises or the async/await syntax that is recommended in the documentation of got.
Only use libraries if they make your life significantly easier, like if you need cookies, TLS etc.

1 Like

Fun fact: this can also be done with the http-request node by default :slight_smile:
In the above, I was merely responding to the suggestion for got specifically, not in the context of the rest of this thread. It's good to have users aware of the consequences of agent-base's mishaps and what it means if you're indeed writing libraries. If future readers were to come onto this topic and only see "got is a good library to use for this" without being aware of the consequences it might have... :frowning:

1 Like

I decided to use xmlhttprequest for the REST calls. There are no special requirements for the http call and it works perfectly in my local network @ home.

The pilight server queries the available devices during startup and the devices will be stored in an associative array for later lookup and correct REST api url creation for the different switches. This array will be stored in the flow context and later on used by the custom node when a REST call has to be executed.
I tried to find any guidance how to name context variables in custom-rules but failed. In order to prevent naming clashes I think it's best to use one object which has the custom-node name as variable name as the context container for the custom-node. Does this make sense or are there other means to prevent naming clashes between different custom-nodes in the contexts?

If you are using the node's context, that is specific to your node so you can name it however you like - if you get a clash, you have only yourself to blame :wink:

It is only if you use a flow or global context variable that you would need to be concerned. Those cases are best avoided altogether. If you cannot avoid it, make the variable name so that it can be chosen in the node's config in the Editor.