Function node: msg properties tab definition

Hi There,

Just out of interest, is there any work being done on definition of msg properties required/used by a function nodes?

What I'm thinking of is a specification of "possible" properties that are accessed on a msg object by a function defined in a function node.

I've been working on something like this for the client code node (i.e. code for the browser) using a tab in the editor panel:

What this tab defines are default values for properties on the msg object - so it plays a double act: defining those properties that the code expects and giving those properties a default value if these aren't set on the msg object.

This is something that might be useful for the function node where it can become non-obvious what "shape" (i.e. which properties) need to be defined on the msg object for the function node to work.

Having a "defaults" tab on the function node would allow defining those properties.

I guess this could be useful for highly visual folk. But it seems like a fair bit of work and complexity for minimal gain for people using function nodes for anything reasonably complex.

I always think that there are basically 2 reasons folk choose to use function nodes:

  1. To collapse down visual logic that has become visually distracting but is mostly stable.
  2. To provide some "business" logic that is hard to do with a collection of nodes.

I can see that some people would consider a way to define a function node's input message shape useful on occasion. Though given that function nodes are essentially textual in nature, I (personally) would have found a textual method of defining it more useful. I think there is already a node that allows you to check and enforce the shape of a message - can't remember the name - so not sure another node would be needed.

An extra tab in the function node itself that allows checking the input msgs and having an output choice of action/event might be more useful?

As for myself, if an input msg of some complexity is needed, I would either add a block comment with an example to the top of the function code or I would add a JSDoc block comment. Most of the time, I know what the upstream output looks like so it isn't often an issue. But occasionally, coming back to older logic, it can be helpful.

Plus it saves a node: no need for a change node to set default values for a function node that might - let's say - takes five property values on the msg object. Set them as defaults, copy & paste function node and have the same default behaviour without an extra change node (or setting values on a inject node).

It would bring the function node closer to a pre-configured in-built node, i.e., just as a debug node that debugs the complete message to the debug panel is a pre-configured debug node.

I find this particular useful if I'm spec'ing out a potential functionality of a future independent node. Replicate the functionality as a function node, play around with the scope of the functionality and then create a separate node package with exactly the same parameters as defined in the default tab.

I can sort of see it useful as pseudo-documentation - but there is no actual enforcement between this and the actual function code - you can still access whatever properties you want, so I'm not sure how much I would gain vs declaring properties defaults at the top of the function - at least then they are all in one chunk of code ( see various other discussions about separate editors/files for functions ) .

My feeling is that once I have reached for the function node - I am now writing code, and if I want docs I'll add them to the docs tab - I don't need yet a third place. But I can see that if you are wanting to enforce schemas or trying to create some tool to auto-convert functions to nodes then it could be useful.

I think there is a third option and that is creating code that is the documentation. I think folks don't think of writing code so that it becomes readable as documentation. That's what refactoring is all about: making code more understandable. (It was also the idea behind COBOL.)

My approach is generally that if I have the feeling that I need to write documentation, then I know my code is a mess and it should be refactored to become more understandable. The reason I do this is because code and documentation have the unfortunate habit of drifting apart - comments tend not to be updated. This then leads to confusion between code and documentation when reading the code. (But this unfortunate habit is largely fixed with AI.)

The only documentation I write is the high-level "what is the purpose" documentation but I avoid detailed documentation on individual code blocks - these should be understandable as code blocks. (This isn't an invitation to debate which amount of code is still understandable - this various across skill levels and does not have a fixed number of code lines - it's an gut feeling and experience thing.)

Of course, this is something that can happen down the road.

Even having highlight inside the editor when using a property that is isn't defined e.g. msg.notdefined would be red underlined in the editor until its defined in the tab. While msg.defined would be ok in the editor.

This doesn't mean it's an error or even warning, it's just a nice to have in the editor. I am not arguing this should become a "must do" or "error if not defined" feature.

It goes in the direction of typification of msg objects - i.e. if a msg has these three properties then it fulfils the type requirements for messages of this this type. (Just as a http-in message must have a .req property for the http-response node - this could be considered that the msg is of type "http request". Or rather, the http-response node takes a message object of type "http request" because it needs the req property.)

For those that might find this interesting, I've experimented with the setting of default values on the msg object. What I've found is that it's useful to define a semantics for setting values on the msg object:

For the client code node, I've now got three different forms of setting values on the msg object: default, overwrite and fixed - what they mean is:

  • "default" - take the value defined and set it on the msg object if and only if the value isn't set on the msg object already
  • "overwrite" - overwrite the value defined on the msg object with the default value but only if the value is defined on the msg object, else ignore the value and leave it undefined
  • "fixed" - always take the value from the default settings regardless whether the value is set on the msg object or not. This is a kind of constant value or good for JSONata expressions such as msg counters.

I guess these kind of semantics could (in part) be also expressed in JSONata using the change node but duplication holds better as the germans say.

Also this is only for the function node, I don't think other nodes should be doing this type of msg manipulation.

(Just passing)

@gregorius

So you are wanting to set message properties - yes?

Why not put a change node just before the function node?

Just a thought.
:slight_smile:

Two reasons:

  1. too many nodes! :wink:
  2. it's also a way of defining what the function requires on the msg object. For long function code, it becomes hard to know what needs to be set on the msg object for the function code to work ...

Think something along the lines of an interface definition in Java for function nodes. That's the intention of the feature and the semantics of what the default values should do is above-and-beyond the actual intention.