Serverless Node-RED instance - tailless not headless!

Hi There!

Having played around with Stackblitz and finding it to be suboptimal, I decided to create a static Node-RED with a very basic dynamic server.

The idea is to give folks the feeling of Node-RED without having to install Node-RED. Ever since I started working with Node-RED, I've been trying to find a good way to get others to have a look at Node-RED. Obviously putting up a full-blown instance is simply an invitation to being hacked, putting up an instance with some kind of signup is a step too far for most. So a static editor version is a good - IMHO - way of introducing Node-RED to non-techies.

The static part is hosted at GitHub while the minimal dynamic server is represented by this flow (the dynamic part is of course done with Node-RED!). The Node-RED editor could be used without a dynamic server but it would be continually throwing errors about not being able to connect to the server. So the flow does three things: provide an endpoint of the websocket communication, provide an endpoint for posts (i.e. deploy succeeds but doesn't change anything) and thirdly it dynamically provides the initial flow.json.

This third part is what I use for now providing a link at flowhub.org for viewing the flows I host there in Node-RED editor - "View in Node-RED" link. For example, the dynamic backend flow for the server can be viewed in Node-RED.

For those who would like to create their own static Node-RED, its easier than one thinks but there are many pitfalls. I ended up modifying the red.js to support static and dynamic content. Ironically playing around with Node-RED in Stackblitz gave many insights so that doing this was relatively easy.

Hope it Helps!

1 Like

Just a quick update:

[{"id":"554fcf2a1a054e30","type":"tab","label":"Flow 1","disabled":false,"info":"","env":[]},{"id":"fce77c1a380e8201","type":"inject","z":"554fcf2a1a054e30","name":"Sample Met Office Data","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[{\"time\":\"2023-11-14T19:00Z\",\"screenTemperature\":10.03,\"maxScreenAirTemp\":10.4,\"minScreenAirTemp\":9.99,\"screenDewPointTemperature\":7.56,\"feelsLikeTemperature\":8.09,\"windSpeed10m\":3.86,\"windDirectionFrom10m\":263,\"windGustSpeed10m\":7.72,\"max10mWindGust\":10.13,\"visibility\":16258,\"screenRelativeHumidity\":84.48,\"mslp\":100360,\"uvIndex\":0,\"significantWeatherCode\":0,\"precipitationRate\":0,\"totalPrecipAmount\":0,\"totalSnowAmount\":0,\"probOfPrecipitation\":0},{\"time\":\"2023-11-15T19:00Z\",\"screenTemperature\":12.13,\"maxScreenAirTemp\":13.2,\"minScreenAirTemp\":9.9,\"screenDewPointTemperature\":7.56,\"feelsLikeTemperature\":10.1,\"windSpeed10m\":4.53,\"windDirectionFrom10m\":254,\"windGustSpeed10m\":8.4,\"max10mWindGust\":11.1,\"visibility\":16365,\"screenRelativeHumidity\":75.36,\"mslp\":100372,\"uvIndex\":0.1,\"significantWeatherCode\":0,\"precipitationRate\":0,\"totalPrecipAmount\":0,\"totalSnowAmount\":0,\"probOfPrecipitation\":0}]","payloadType":"json","x":160,"y":60,"wires":[["c6624545f4820893","eb1eb4ae3f46518e"]]},{"id":"c6624545f4820893","type":"ui-chart","z":"554fcf2a1a054e30","group":"e23512b61ee4b0a2","name":"","label":"Feel Like Temperature","order":9007199254740991,"chartType":"line","category":"feels-like","categoryType":"str","xAxisProperty":"time","xAxisPropertyType":"msg","xAxisType":"time","yAxisProperty":"feelsLikeTemperature","ymin":"","ymax":"","showLegend":false,"removeOlder":1,"removeOlderUnit":"3600","removeOlderPoints":"","colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"width":6,"height":8,"className":"","x":480,"y":60,"wires":[[]]},{"id":"eb1eb4ae3f46518e","type":"ui-chart","z":"554fcf2a1a054e30","group":"e23512b61ee4b0a2","name":"","label":"Multiple Lines Example","order":9007199254740991,"chartType":"line","category":"[\"screenTemperature\", \"feelsLikeTemperature\"]","categoryType":"json","xAxisProperty":"time","xAxisPropertyType":"msg","xAxisType":"time","yAxisProperty":"","ymin":"","ymax":"","showLegend":true,"removeOlder":1,"removeOlderUnit":"3600","removeOlderPoints":"","colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"width":6,"height":8,"className":"","x":490,"y":100,"wires":[[]]},{"id":"8864477d6b15168d","type":"inject","z":"554fcf2a1a054e30","name":"Clear","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[]","payloadType":"json","x":210,"y":100,"wires":[["c6624545f4820893","eb1eb4ae3f46518e"]]}]

can be copied and then opened https://cdn.flowhub.org by using "import flow" menu point and then pasting into the dialog.

It's a useful thing to have a fully functional Node-red playground.

Could I achieve the same thing locally using Node-red on a Raspberry Pi with the overlay filesystem enabled, rebooting every day, so any changes made would be rolled back?

I recall some years ago where someone created node-red to actually run in the browser all the way, as for many things, no backend is 'truly' necessary. The project died off silently though.

Sure, are you going to put it up on the Net? It would be much better to have something that does have a server, I agree. I guess that's for those that come after me.

Something similar to what BabylonJS has or Mermaid has but it's not that simple with Node-RED, either of those have a server side.

Do you mean this Node-Red in Browser via Stackblitz - that was last year? I played around with that and it does semi-work but it's very fragile and only really works with Chromium browsers.

I have other reasons for creating this "in browser" Node-RED, it's actually not that important that there is no server - at least for my purposes.

Well it's still hosted - GitHub - monteslu/pagenodes: Completely Browser Based IOT Platform
and @Monteslu is still very active on other projects :slight_smile:

2 Likes

https://pagenodes.com/ ==>

Screen Shot 2023-12-18 at 19.58.52

Could it be hosted on GitHub as a pages thing?

Being six years old, which version of Node-RED would that be?

Pity about all the work that went into that creating browser workers to simulate the server.

Ok I accepted the certificate and now the NSA noticed I did this:

Is that original UI from Node-RED 2018?

Edit:

How do I use the Sentiment node?

Screen Shot 2023-12-18 at 20.12.58

:wink:

If you accept the risk it's still there -

Can't recall the version but yes very old... and yes @monteslu is/was a beast.

1 Like

All respect to everyone involved - it actually allowed me to import a 2023 flow and it displayed correctly.

1 Like

Ah yes. It would have to be thought out carefully, probably involving many buzzwords and acronyms.

Fortunately, it's not easy to put my network on the Web because it's behind carrier grade Nat.

And there's a fistful of jargon already!

Wow! Whatever happened to the oscillator node?

I created this flow:

[{"id":"ZnCarn2EMvc","type":"inject","z":"kZRMsOFyOcU","name":"","topic":"","payload":"hello world","payloadType":"str","repeat":"","crontab":"","once":false,"allowDebugInput":false,"x":659,"y":549,"wires":[["uNYQrOQlQ48","wzZ9LUvgHyM","55mW_ID9RX8","7BU5MB1m0zE"]]},{"id":"uNYQrOQlQ48","type":"oscillator","z":"kZRMsOFyOcU","name":"","duration":500,"frequency":440,"shape":"sine","active":true,"x":1049,"y":666,"wires":[]},{"id":"wzZ9LUvgHyM","type":"delay","z":"kZRMsOFyOcU","name":"","pauseType":"delay","timeout":"500","timeoutUnits":"milliseconds","rate":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":788,"y":731,"wires":[["2PEw6d-_ARc","xpjzOGHaxto","0NfHRCI-LsI","c65GSyZSqj4"]]},{"id":"55mW_ID9RX8","type":"delay","z":"kZRMsOFyOcU","name":"","pauseType":"delay","timeout":"400","timeoutUnits":"milliseconds","rate":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":727,"y":796,"wires":[["Uj5RKjRuReY"]]},{"id":"7BU5MB1m0zE","type":"delay","z":"kZRMsOFyOcU","name":"","pauseType":"delay","timeout":"350","timeoutUnits":"milliseconds","rate":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":680,"y":872,"wires":[["vb_oH2qrT_g"]]},{"id":"2PEw6d-_ARc","type":"oscillator","z":"kZRMsOFyOcU","name":"430","duration":500,"frequency":"430","shape":"sine","active":true,"x":1039,"y":759,"wires":[]},{"id":"xpjzOGHaxto","type":"delay","z":"kZRMsOFyOcU","name":"","pauseType":"delay","timeout":"350","timeoutUnits":"milliseconds","rate":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":970,"y":797,"wires":[["E4uUNUrlKlo"]]},{"id":"0NfHRCI-LsI","type":"delay","z":"kZRMsOFyOcU","name":"","pauseType":"delay","timeout":"400","timeoutUnits":"milliseconds","rate":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":1033,"y":717,"wires":[["d4MlNQqNe8Q"]]},{"id":"c65GSyZSqj4","type":"delay","z":"kZRMsOFyOcU","name":"","pauseType":"delay","timeout":"500","timeoutUnits":"milliseconds","rate":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":1105,"y":602,"wires":[["ejh48qVqMp4","bueYWOFfyAo","gSrp32e8__E","9KkY1_6bbYE"]]},{"id":"Uj5RKjRuReY","type":"oscillator","z":"kZRMsOFyOcU","name":"430","duration":500,"frequency":"420","shape":"sine","active":true,"x":971,"y":840,"wires":[]},{"id":"vb_oH2qrT_g","type":"oscillator","z":"kZRMsOFyOcU","name":"430","duration":500,"frequency":"410","shape":"sine","active":true,"x":904,"y":903,"wires":[]},{"id":"E4uUNUrlKlo","type":"oscillator","z":"kZRMsOFyOcU","name":"430","duration":500,"frequency":"340","shape":"sine","active":true,"x":1205,"y":830,"wires":[]},{"id":"d4MlNQqNe8Q","type":"oscillator","z":"kZRMsOFyOcU","name":"430","duration":500,"frequency":"390","shape":"sine","active":true,"x":1226,"y":755,"wires":[]},{"id":"ejh48qVqMp4","type":"oscillator","z":"kZRMsOFyOcU","name":"430","duration":500,"frequency":"380","shape":"sine","active":true,"x":1232,"y":675,"wires":[]},{"id":"bueYWOFfyAo","type":"delay","z":"kZRMsOFyOcU","name":"","pauseType":"delay","timeout":"350","timeoutUnits":"milliseconds","rate":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":909,"y":468,"wires":[["SmvQYzfQTUY"]]},{"id":"gSrp32e8__E","type":"delay","z":"kZRMsOFyOcU","name":"","pauseType":"delay","timeout":"400","timeoutUnits":"milliseconds","rate":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":956,"y":392,"wires":[["PmFlSVuoxTs"]]},{"id":"9KkY1_6bbYE","type":"delay","z":"kZRMsOFyOcU","name":"","pauseType":"delay","timeout":"500","timeoutUnits":"milliseconds","rate":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":1017,"y":327,"wires":[["aS_xapK3jjs"]]},{"id":"SmvQYzfQTUY","type":"oscillator","z":"kZRMsOFyOcU","name":"430","duration":500,"frequency":"410","shape":"sine","active":true,"x":1133,"y":499,"wires":[]},{"id":"PmFlSVuoxTs","type":"oscillator","z":"kZRMsOFyOcU","name":"430","duration":500,"frequency":"420","shape":"sine","active":true,"x":1200,"y":436,"wires":[]},{"id":"aS_xapK3jjs","type":"oscillator","z":"kZRMsOFyOcU","name":"430","duration":500,"frequency":"430","shape":"sine","active":true,"x":1241,"y":358,"wires":[]}]

and now my neighbours are complaining!

thanks for the kind words, dceejay

I really gotta update this. It's pretty far back from node-red main now, and needs https certs updated so it can use secure web APIs again.

I did a bunch of work for communcation with webcontainers. Been thinking about doing a new build of mainline node-red + webcontainers + browser APIs

2 Likes

Great work :+1:

Can't you put this up as a github pages thing? I did notice the CNAME file, was that an attempt at using github?

Btw just stole your oscillator node and will bring it back :wink:

1 Like

For those who desire to make sound in their browser using Node-RED, check out this flow (in Node-RED view) - the flow uses the client code node for interfacing with the browser, the browser code then uses the WebAudio API to create the sounds. I've added fade-in/out to avoid crackling but your mileage might vary.

I get this error, when firing the inject node using google chrome browser on a laptop.

execution

Feature not bug, it's a serverless node Red so nothing can be executed. It's only for the visual feel when using node Red.

But what you can do is view the code beyond behind the nodes, make changes and then export the flow to import into a serverful node Red instance.

Edit: beyond --> behind

1 Like

Shouldn't that be an "expected" error then, not an unexpected one? :wink:

Well I knew it was a feature, so there is no talk of bug/error from me! :wink: Btw that's also a feature, not a bug!

Philosophical question: what's the difference between error and bug? A user has an error and a programmer finds the bug?