UBOS Community

vova
vova

Posted on

How to use the "function" node?

The function node allows you to execute JavaScript code for messages that are passed through it. The message is passed as an object named msg, and by default, it will have a msg.payload property that contains the body of the message.

Other nodes can add their own properties to the message. They should be documented in their respective documentation.

Writing Functions

The code entered in the function node represents the body of the function. The simplest function returns the message as it is.

return msg;
Enter fullscreen mode Exit fullscreen mode

If a function returns null, the message is not passed, and the flow terminates.

The function must always return an msg object. Returning a number or a string will result in an error.

The returned message object does not necessarily have to be the same object that was passed. The function can construct a new object before returning it. For example:

Image description

Note: Creating a new message object will result in the loss of any properties from the received message. This can break certain flows, such as the HTTP In/Response flow, which requires the mandatory preservation of the msg.req and msg.res properties. In general, function nodes should return the message object they received after making any desired changes to its properties.

Example of how it should be done

Let's create a flow that returns the value of the request body. We'll use the http in and http response nodes (more details about them here).
Image description

The http in node sends a message to the function node with properties like payload, req, and res. In the body of the function, we only need to update the specific value we want in msg.payload without replacing the entire msg object. Then the http response node will understand that it needs to return the response to our request.

Let's make a request using Postman.

Image description

We received the text that was passed in the request body as the response.

Example of how it should not be done

Let's modify the body of the function in the above-described flow to:
Image description
In the console, we will receive the message "No response object". This happens because we created a new message object, and the http response node doesn't know that it needs to return the response.

Therefore, if you need to create new properties, you should assign them to msg without creating a new object.

Multiple Outputs

In the function node's editor, there is an option to change the number of outputs and the name of the function.
Image description
Let's add two outputs and change the name of the function.
Image description
Now the function node has two outputs. The question arises: how do we control them? We need to use an if statement and return an array of messages in its body. The number of messages in the array depends on the number of outputs, which are numbered from top to bottom.

if (msg.payload.length > 0) {
    msg.payload = `array length: ${msg.payload.length}`;

    return [ null, msg ]
}

msg.payload = "Empty"
return msg;
Enter fullscreen mode Exit fullscreen mode

For example, let's consider that we have two outputs: return [null, msg]. In the first output, we return null, indicating that the function doesn't return anything on that output in this case. In the second output, we pass msg. Therefore, if an array with multiple elements is passed to the function, the second output will return a string representing the length of the array. Otherwise, we return return msg on the first output.

Image description

Let's add another function node called "data" that returns an array of objects.

msg.payload = [
    { name: "banana" },
    { name: "potato" }
];

return msg;
Enter fullscreen mode Exit fullscreen mode

Image description

We received true, so the message was sent to the second output. If we pass an empty array to the "data" function, the condition will not be met, and the message will be sent to the first output.

msg.payload = [];
return msg;
Enter fullscreen mode Exit fullscreen mode

Image description

Event Logging

To effectively debug your code, it is important to understand what data is coming in and how it changes. For this purpose, you can use the following functions to log messages to the console (similar to console.log() in JavaScript).

node.warn("Something happened");
node.error("Something bad happened");
Enter fullscreen mode Exit fullscreen mode

Let's modify the example we discussed earlier. We'll add a loop that iterates over an array and prints its elements to the console.

if (msg.payload.length > 0) {
    node.warn("start if");

    for (const element of msg.payload) {
        node.warn(element);
    }

    return [ null, msg ]
}

msg.payload = "Empty"
return msg;
Enter fullscreen mode Exit fullscreen mode

Image description
You can see 3 node.warn events in the console.

Error Handling

If a function encounters an error and needs to terminate the execution flow, it should not return anything. To trigger a catch at a specific node, the function should call node.error("error", msg), passing the error message as the first argument and msg as the second argument.

Let's modify the previous example by adding an error to the _data_ function.

msg.payload = [
    { name: "banana" },
    { name: "potato" }
];

node.error("Еrror", msg);
Enter fullscreen mode Exit fullscreen mode

Image description

The _data_ function returned an error, so the node stopped working.

Manage Palette

To add additional Node-RED nodes to your project, you need to open the menu on the right and select the "Manage Palette" tab.
Image description The "Nodes" tab contains the libraries that are already loaded, and in the "Install" tab, you need to enter the name of the library and install it.

Before installing a library, it is advisable to familiarize yourself with its documentation.

Image description

After installation, the newly installed nodes will appear in the list of all nodes.

Image description

NPM package

Node-RED allows you to fetch additional modules from the Node Package Manager (NPM). To do this, select the "function" node where you want to use the additional module and go to the "Setup" tab. In this tab, click the "add" button and enter the module you want to use. If such a module exists on the NPM platform, we can use it.

Let's create a flow that takes an image, optimizes it, and flips it.

[Image] -> [Optimization Module] -> [Flip Module]
Enter fullscreen mode Exit fullscreen mode

In this flow, the "Image" node represents the input image, the "Optimization Module" performs image optimization, and the "Flip Module" flips the optimized image.
Image description

Let's use the new "viewer" node that we previously installed.

Note: To work with images, it is recommended to use the POST method and enable image uploads.

Image description

Let's add the sharp module for working with images.
Image description У тіло функції додаємо логіку роботи з зображенням.

const optimized = await sharp(msg.req.files[0].buffer)
    .toFormat("jpeg", { quality: 60 })
    .flip()
    .toBuffer();

msg.payload = optimized;

return msg;
Enter fullscreen mode Exit fullscreen mode

We will send the image using Postman. The request body will be of type form-data. For the image, select the file type.
Image description

The result of the execution
Image description
The request sends an image buffer stored at the following path: msg.req.files[0].buffer. The weight of the sent image is size: 414164 (404 KB).
Image description The converted image weighs 292563 (285 KB).
Image description

Discussion (0)