Node dev : getNodeParameter usage?

If I don’t mistake …

const myBrand = this.getNodeParameter('brand', 0) as string;

will always set the content of the propery ‘brand’ of current node to the variable myBrand
the ‘0’ value doesn’t seem to be used

But about this loop on input items ?

for (let i = 0; i < items.length; i++) {
        ...
const type = this.getNodeParameter('type', i) as string;

I understand that if the property ‘type’ is empty, the node gets it from its input (previous node)
But the value i is arbitrary no ? … not clear at all sorry

The method getNodeParameter() returns the value of the parameter and only the parameter. So if the parameter is empty it will return empty no matter what.

The number after the parameter-name is the index of the item which should be used to resolve the expression (if one is set).

Example:
If we have a parameter called “firstName” and it has an expression like this: {{json["name"]}}
and the node receives two items:

  1. [index 0] “name” set to: “Jim”
  2. [index 1] “name” set to: “Frank”

Would it resolve like this:

this.getNodeParameter('firstName', 0) => Jim
this.getNodeParameter('firstName', 1) => Frank

I hope that makes sense.

Thanks jan, sorry not clear yet … maybe I’m not clear myself :no_mouth:
Lexicon :
“property” => got from the node form, for example “Request method” in the Http-request node
“parameter” => got from the previous node output, it can be read in $json for example in a Function node
Getting a property is easy :

const myBrand = this.getNodeParameter('brand', 0) as string;

This works, here the index is 0, so it means something, but I admit that this arg “is not set” as you say. So yes I get the value of ‘brand’ set in the node form.

Now imagine this property ‘brand’ is optional in my node, because it can be read as a parameter from the previous node (yes, some properties can be dynamic depending of the state of the WF), so I suppose the input like this :

 {
    "other" : "whatever",
    "brand" : "philips",
   
     etc ...
}

So I understand that the ‘parameter’ method :

const myBrand = this.getNodeParameter('brand', 0) as string;

will return undef/empty.
But, during the loop on items, the variable myBrand with take the value ‘philips’ when index will be 1, and won’t be affected if i != 1. So we loop because we don’t know the order or parameters. Right ?

But … imagine “brand” is at the first position in the json input, the get-property will work as a get-parameter because index 0 is right, so the variable myBrand, if it has been set in the form, will be overwritten with the parameter value, no?

I think you are mixing up two different things.

An item is the whole object. So if a node receives this input data:

[
	{
		json: {
    			"brand" : "philips",	
	    		"other" : "whatever1"
		}
	},
	{
		json: {
	    		"other" : "whatever2"
		}
	}
]

It would have two items. So if you write this code:

const items = this.getInputData();

You will have in items EXACTLY the above array.
Here the documentation about the n8n datastructure:
https://docs.n8n.io/reference/data/data-structure.html#data-structure

If your node has a parameter called “brandName” with the following expression set: X{{ $json["brand"] }}X and you request its content via getNodeParameter you will get the following output:

this.getNodeParameter('brandName', 0) => "XphilipsX"
this.getNodeParameter('brandName', 1) => "XX"

By default there is no relationship between the node input data (the data it receives from the previous node) and the parameters (the parameters which are set on the node) at all. They are totally independent. So if you simply set the node parameter “brandName” to “test” you would always get “text” back, no matter which index you supply. Only if you set an expression on it will you maybe receive different data depending on the index you supply (depending on the expression).

So the index you supply tells n8n which item (so the whole object, in the above example there are two different ones) should be used to evaluate the expression.

Not so simple … the same method name for node properties and node input is not a good choice in my opinion …
So, taking you case, and if there’s a field “brandName” in the form, am I right ?
1/ the user typed “philips” in the form => this.getNodeParameter(‘brandName’, whatever) returns “philips” even if this parameter exists in items
2/ the field is empty (empty string) => I have to loop with this method on items to fetch the content of “brandName” in input, but I must stop the loop when I see that the current index give to my variable a value

That is not the case and I explained that above.

Get Node-Properties:

this.getNodeParameter('parameterName', 0)

Get Node-Input:

this.getInputData();

About your questions:

  1. correct
  2. Do not understand - But you do not have to do anything. If the user left it empty then it is maybe supposed to be empty. And just because a parameter has the same name as data from the input data does not mean it is supposed to be used as such. The user has to decide that, for that reason can he create an expression and so specifically say that he wants that data to be used as “brand Name”

For 2/ my node can have several "previous-node"s which set their own value for a property which will remain empty indeed in the form. I don’t know it in advance. But for other WF, the user may set this value in the form.
So I must fetch this property value in the items values (yes obtained with this.getInputData())
But I still don’t know how to do because I do not kown where it is located in items[]
For example :

[
	{
		json: {
    			"day" : "monday",	
	    		"other" : "whatever1"
		}
	},
	{
		json: {
                   "other" : "whatever2",
                   "brand" : "philips",
		}
	}
]

and

[
	{
		json: {
    			"brand" : "toyota",	
              }
    }
]

What do you mean with?

my node can have several "previous-node"s

Does the node have multiple inputs? Because if it only has one it should not matter if there is one node before your custom one or multiple. What matters is what the node directly before the current one outputs and nothing else.

Sereval arrows can reach this destination node, with different outputs, but only one of them will be activated in the WF.
Even without this context, so only one node before, I must predict that its form parameters come from this node ouput.

Still so not understand what should be predicted. Even if multiple nodes would connect to the node and all are active there is nothing to perdict.

Let’s assume 3 nodes are connected to the node. It would then execute once with the data of the first node, once with the data of the second and once with the data of the third. The data of all the previous nodes would never end up in the execute function at the same time. It is only possible to use data from multiple nodes at the same time if there are different inputs.

Yes I agree jan, and yes only data from a unique previous node is considered, since only this previous node is activated during one execution of a WF.
But this is not my problem : imagine there’s only one previous node which outputs data.
My problem is how to read previous node output, once we getInputData().
Let’s My current node has “brandName” and “age” in its form.
Please, and it would be the end, could you give me the ts code as a whole which :

  • read form fields “brandName” and “age”
  • if set by user in form, keep the value, if not, read the value from inputs items (with loop ?)

and which convention must follow the previous node to arrange these values in its ouput json ?

Here an example which does exactly that:

import { IExecuteFunctions } from 'n8n-core';
import {
	INodeExecutionData,
	INodeType,
	INodeTypeDescription,
} from 'n8n-workflow';


export class Example implements INodeType {
	description: INodeTypeDescription = {
		displayName: 'Example',
		name: 'example',
		group: ['input'],
		version: 1,
		description: 'Example',
		defaults: {
			name: 'Example',
			color: '#0000FF',
		},
		inputs: ['main'],
		outputs: ['main'],
		properties: [
			{
				displayName: 'Age',
				name: 'age',
				type: 'number',
				default: 0,
			},
			{
				displayName: 'Brand Name',
				name: 'brandName',
				type: 'string',
				default: '',
			},
		]
	};


	execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {

		const items = this.getInputData();

		if (items.length === 0) {
			items.push({json: {}});
		}

		const returnData: INodeExecutionData[] = [];
		let item: INodeExecutionData;

		for (let i = 0; i < items.length; i++) {
			const age = this.getNodeParameter('age', i) as number | undefined;
			const brandName = this.getNodeParameter('brandName', i) as string | undefined;

			item = items[i];

			const newItem: INodeExecutionData = {
				json: {
					age: age || item.json.age,
					brandName: brandName || item.json.brandName,
				},
			};

			returnData.push(newItem);
		}

		return this.prepareOutputData(returnData);
	}
}

But that was really just an exception as we do not do free custom node development!

and which convention must follow the previous node to arrange these values in its ouput json ?

Again do not understand. There is simply no sorting in a JavaScript-Object.

Thanks a lot jan, I better understand this method.
Just a thing which confuses me, if first items in input json are for example “date” and “color”, won’t you get 2 undefined values in 2 iterations, and build a quite empty json in corresponding newItem, which is pushed anyway ? Or must I understand that the method will randomly get the good item in input json ? In short, I guess that there will be items.length items of json in output. I would use a ‘continue’ and would push only once at the loop exit, no ?
Something like that :

let ageKept: number;
let brandNameKept: string;
for (let i = 0; i < items.length; i++) {
         const age = this.getNodeParameter('age', i) as number | undefined;
         const brandName = this.getNodeParameter('brandName', i) as string | undefined;
         // several age or brandName are possible in input json, we take the last seen
         if (age != undefined) { ageKept = age; continue; }
         if (brandName != undefined) { brandNameKept = brandName; continue; }
}

// TODO test on undefined *Kept variables
const newItem: INodeExecutionData = {
      json: {
            age: ageKept,
            brandName: brandKept,
      },
};

returnData.push(newItem);

Yes exactly. If the input data does not have “age” and “brandName” and it is also not set as a parameter on the node both of them would be undefined. Which can be right or wrong depending on what exactly you are doing.

If one item would contain “date” and “age” it would result in one empty item. If there is an item with “date” and another item with “age” then it would return in two. So the node currently simply outputs exactly as many items as it receives.

Not sure what you mean with randomly. Nothing happens anywhere randomly.

The code you wrote would always return only one single item (no matter how many input items there were). Also does it totally ignore the data on the item and only reads it from the parameters. Which is now totally different from your above requirements. Additionally would it not read the “brandName” of an item if it has “age” set. Not sure if that is really what you want. If not the both “continue” can simply get deleted.

Randomly = direct access. I mean : does the method will return age if age is in the input json, whatever the index in the method parameter and the location of age in the json ? I don’ think so ?
Yes the 2nd continue is useless of course, the first just avoids a useless test on brandName.