Node-RED: Creating a custom node using TypeScript
Welcome! In this brief article, I will outline how to create a custom Node-RED node using TypeScript.
I have published a small library that allows TypeScript to be used in Node-RED. If you just want to use it, you can find it here. If you are interested in how it works, I invite you to keep reading!
Node-RED is a programming tool for wiring together hardware devices, APIs and online services in new and interesting ways.
Motivation
Work on my Master’s thesis gravitated towards creating some Node-RED nodes. Being an avid fan of TypeScript and its benefits, I decided to research the topic of developing nodes using the language, instead of the recommended ES5 “classes”. This article outlines how I achieved a solution.
Introduction
A custom Node-RED node is mainly comprised of a .js
and a .html
file. The goal of this post is to show how to replace the js
file with a ts
file by leveraging the knowledge of how JavaScript works and making use of the TypeScript compiler.
Note: As of the writing of this article, I haven’t found a way to replace the JavaScript in the HTML file with TypeScript. Anyhow, the amount of JavaScript in this file is generally so reduced that the benefit earned by using TypeScript in this context would be small.
Understanding the Vanilla Way
Reusing the example present in the official documentation, we will be building a node that transforms the messages it receives into all lower case letters.
For this, two files are necessary, namely lower-case.html
and lower-case.js
. Their function is outlined very well in the documentation:
[Nodes] typically consist of a pair of files; a JavaScript file that defines what the node does, and an HTML file that defines the node’s properties, edit dialog and help text.
The HTML file configures the settings and the interface displayed in the Node-RED’s web UI and, as such, is not very interesting for the purpose of this article.
The JavaScript file is the one that effectively implements the logic of the node and so this is the one we’ll be looking into more closely.
This is how the official documentation requires a node to be structured:
module.exports = function(RED) {
function LowerCaseNode(config) {
RED.nodes.createNode(this,config);
var node = this;
node.on('input', function(msg) {
msg.payload = msg.payload.toLowerCase();
node.send(msg);
});
}
RED.nodes.registerType("lower-case", LowerCaseNode)
}
For new users of JavaScript, this syntax may seem weird. In fact, it was how classes were written before ECMAScript 6 came along. You can read more about pre-ES6 classes and prototypes here.
Long story short, the LowerCaseNode
function is acting as a constructor of the LowerCaseNode
class. Inside the function, this
refers to the object being constructed.
Node-RED will then add some properties to the prototype of LowerCaseNode
, such as the on()
function we see on line 5. We will be leveraging this knowledge in order to use classes in TypeScript.
It should also be noted that we are passing the constructor of the class as the second argument of the registerType
function, on line 10.
Given these two pieces of knowledge, we can force the TypeScript compiler into generating the code we want.
Onto TypeScript
The TypeScript compiler not only transforms TypeScript code into JavaScript, but is also capable of selecting the version of ECMAScript to compile to, by utilizing the target
option present in tsconfig.json
.
By setting the target
option to "es5"
, we can obtain the ES5 code Node-RED expects.
Having updated tsconfig.json
, we can finally dive into the actual code.
As said before, we’ll be using a class to write the LowerCaseNode
. This requires the constructor to have the same signature (line 3). Also, inside the constructor, we can now use this
, instead of the previous var node = this;
workaround.
Finally, we have to call registerType
passing the class itself as the second argument.
module.exports = function (RED: Red) {
class LowerCaseNode {
constructor(config: NodeProperties) {
RED.nodes.createNode(this, config);
this.on('input', function(msg: any) {
msg.payload = msg.payload.toLowerCase();
this.send(msg);
});
}
}
RED.nodes.registerType("lower-case", LowerCaseNode)
}
If we compile the above example, we’ll get the following compiled JavaScript code:
module.exports = function (RED) {
var LowerCaseNode = /** @class */ (function () {
function LowerCaseNode(config) {
RED.nodes.createNode(this, config);
this.on('input', function (msg) {
msg.payload = msg.payload.toLowerCase();
this.send(msg);
});
}
return LowerCaseNode;
}());
RED.nodes.registerType("lower-case", LowerCaseNode);
};
Note: You can play with the code on the TypeScript Playground to see how changes affect the output.
We can observe that the output is almost identical to the official Node-RED example. There is, however, a small change. The variable LowerCaseNode
(line 2) now refers to an Immediately Invoked Function Expression (read more) that returns the constructor of the LowerCaseNode
.
In the end, the difference is not problematic and the node will work as expected.
This concludes the small proof of concept that illustrates how to use Node-RED with TypeScript.
Conclusion
Hopefully, not only did you understand how to write a Node-RED node in TypeScript, but also why and how it works.
You can now test your own components in Node-RED using the constructs mentioned in this article.
Thank you for reading 👏
If you are interested in using a more higher-level (although still very basic) library, you can check the one I published.
Make sure to leave feedback, suggestions and/or corrections.