Best practice for the msg object?

What is the recommendation for dealing with the msg object. In the on Input event we get the msg object. Typically we might only be interested in the payload, but the msg properties might have items that need to be passed along like the response object of the http node.

So do I:

let reply = { payload: myFunc()}
send(reply)

or

msg.payload = myFunc();
reply(msg)

Perhaps you should start be carefully rereading the Function Documentation. (I must have reread this a dozen times myself and still pick up subtleties on each reread).

The returned message object does not need to be the same object as was passed in; the function can construct a completely new object before returning it.

However, there are sometimes when nodes (e.g.split) or your own function code wants to add additional properties to pass extra state / context to downstream nodes, so in general if your node emits a single message, then it is safer just to update the message properties and return the updated message at the end. This type of function can safely be used in sequence node, implementing an HTTP Service etc. And I use this a lot by adding msg.state if I am looping back through a helper MySQL or HTTP Request node or simply passing stuff to another function node downstream.

If you don't want to pass through properties then just build the message as the Function guide suggests.

Also note that you don't need to use node.send() to send multiple outputs, as the guide also describes how you can use 1 and 2 dimensional message arrays to send multiple messages to multiple outputs through a single return msgArray; Just build the message array as needed and return it on exit

In general you only need to use node.send() if your function does asynchronous processing, that is it uses asynchronous callback functions such as setTimout(), or Promises and/or the keywords async, await. If you have used these, then you do need to use node.send(). This being said, at a runtime level, your function code is wrapped by the Node-RED runtime as an async function so you do have the rich async feature set available if you do need it. There are lots of decent tutorials on YouTube and the wider Internet if you want to understand more about this.

If you do use node.send(), then the Node-RED runtimes will assume that your code is doing asynchronous processing, so in this case you should always follow the last send by a node.done() so that the runtime knows that this execution instance is completed.

PS. I missed the Developing Nodes tag. This answer really relates to "Developing Functions" rather than Nodes. The differences are sufficient that this is misleading and the Q is better addressed by others. Sorry, my bad.

Always try to return the original msg.

Consider someone uses your node between the http-in and http-response - the msg MUST have the original properties In order for the endpoint to work. Same goes for the TCP and split/join nodes.

Also, a common/recommended pattern (a PURE ish pattern) is to pass all properties needed in a msg to a function/flow/link/subflow. By discarding the original message, a user of your node would have to resort to storing the msg (or it's properties that are important and necessary for downstream nodes) before passing the message through your node, then picking them up again afterwards. If your node is async, this pattern will eventually catch the user out when fast consecutive messages result in inconsistencies.

let reply = { payload: myFunc()}
return reply

would overwrite the whole object, therefore losing any response properties

msg.payload = myFunc();
return msg

would just overwrite payload, saving any response properties.

p.s have moved category as I don't think this is about developing a node. If I am wrong let me know and will reverse move.

@E1cid I was unsure to but for the following clues:

  1. Was posted in Developing Nodes

  2. Speaks of the .on('input' event

  1. Mentioned send(...)
1 Like

Missed that, cheers have returned to developing nodes

1 Like

Hi all,

appreciate the replies. In my actual case I do use async processing, so send is needed. @TerryE thx for the pointer to node.done(). Which opens an interesting followup question. I do

fetch(someurl, options)
  .then(result => result.body) // A [readableStream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream)
.then(body => body.pipeThrough(new TextDecoderStream())
       .pipeTo(streamToSend(msg, send))
.catch(e => node.error(e));

the fetch returns a chunked response so the streamToSend is called a few times...

const streamToSend = (returnMsg, send) => {
  let sequence = 0;
  return new WritableStream({
    write(data) {
      sequence++;
      send({ ...returnMsg, seq: sequence, payload: data });
    }
  });
};

How would I fit the node.done() into it?

P.S.: Node will be public soon

When the fetch is done

e.g.

  • request
  • receive part 1 of 3 -> send
  • receive part 2 of 3 -> send
  • receive part 3 of 3 -> send
  • done()
1 Like

Yup, logically the "done" is just another message; however

  • It must be issued only once.
  • It must be issued after all other messages have been sent
  • In this case it is not forwarded to the next node, but instead it is used by the runtime to determine the node's execution, that is to turn its running state from true to false.

In the case of user functions, the runtime actually scans the function source when deploying it to determine whether the function is asynchronous. (See 10-function.js:L139). Module writers are expected to understand and to follow the rules.

PS. I am not sure what your indenting of the first then function is supposed to imply. This is just a flat promise chain.

1 Like

"Best practice" is "first, do no harm." The documentation on Creating Nodes is pretty unambiguous about this:

If the node is sending a message in response to having received one, it should reuse the received message rather than create a new message object. This ensures existing properties on the message are preserved for the rest of the flow.

The only issue I can see arises if your node uses specific properties or values (for example, msg.topic, msg.reset, or msg._control) as part of its logic. Passing these along could affect the operation of either other nodes that use these properties differently or other instances of your own node. So the choice of how these are passed along should be clearly documented or made options in the configuration dialog.

1 Like

Thx for the hint. Turns out I had the done() in my wrapper code already

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.