| name | n8n-syntax-node-types |
| description | Use when creating custom n8n nodes, defining node properties, implementing execute methods, or building declarative nodes. Prevents incorrect INodeProperties types and malformed displayOptions conditions. Covers INodeType interface, INodeTypeDescription, INodeProperties (22 types), displayOptions with rich conditions, execute() method with IExecuteFunctions, Node base class alternative, versioned nodes, declarative routing, and methods (loadOptions, listSearch, credentialTest, resourceMapping). Keywords: n8n, custom nodes, INodeType, INodeProperties, execute,, create custom node, INodeType, node properties, dropdown options, execute method. displayOptions, declarative routing, versioned nodes, credentialTest.
|
| license | MIT |
| compatibility | Designed for Claude Code. Requires n8n v1.x. |
| metadata | {"author":"OpenAEC-Foundation","version":"1.0"} |
n8n-syntax-node-types
Quick Reference
INodeType Interface Members
| Member | Signature | Required | Purpose |
|---|
description | INodeTypeDescription | YES | Node metadata, properties, credentials |
execute | (this: IExecuteFunctions) => Promise<INodeExecutionData[][]> | For regular nodes | Process input items |
trigger | (this: ITriggerFunctions) => Promise<ITriggerResponse> | For event triggers | Emit data on events |
poll | (this: IPollFunctions) => Promise<INodeExecutionData[][] | null> | For polling triggers | Periodically check for data |
webhook | (this: IWebhookFunctions) => Promise<IWebhookResponseData> | For webhook nodes | Handle HTTP requests |
supplyData | (this: ISupplyDataFunctions, itemIndex: number) => Promise<SupplyData> | For AI sub-nodes | Provide data to AI agents |
methods | { loadOptions, listSearch, credentialTest, resourceMapping, actionHandler } | No | Dynamic methods |
webhookMethods | { [name]: { checkExists, create, delete } } | No | External webhook lifecycle |
INodeTypeDescription Key Fields
| Field | Type | Required | Notes |
|---|
displayName | string | YES | Human-readable name in UI |
name | string | YES | Internal identifier (e.g., 'myNode') |
icon | string | { light, dark } | No | 'fa:icon-name' or 'file:icon.svg' |
group | NodeGroupType[] | YES | 'input', 'output', 'transform', 'trigger', 'schedule' |
version | number | number[] | YES | Supported versions |
description | string | YES | Short description |
defaults | { name: string } | YES | Default node name |
inputs | NodeConnectionType[] | ExpressionString | YES | Input connections |
outputs | NodeConnectionType[] | ExpressionString | YES | Output connections |
credentials | INodeCredentialDescription[] | No | Required credential types |
properties | INodeProperties[] | YES | Node parameters |
polling | true | No | Marks node as polling trigger |
webhooks | IWebhookDescription[] | No | Webhook registrations |
|
All 22 Property Types (NodePropertyTypes)
| Type | UI Element | Default Value Type |
|---|
'boolean' | Toggle switch | boolean |
'button' | Action button | string |
'collection' | Group of optional fields | {} |
'color' | Color picker | string |
'dateTime' | Date/time picker | string |
'fixedCollection' | Group with fixed structure | {} |
'hidden' | Hidden value | string |
'icon' | Icon selector | string |
'json' | JSON editor | string |
'callout' | Info/warning callout | string |
'notice' | Notice/info text | string |
'multiOptions' | Multi-select dropdown | string[] |
'number' | Number input | number |
'options' | Single-select dropdown | string |
'string' | Text input | string |
'credentialsSelect' | Credential selector | string |
'resourceLocator' | Resource locator (ID/URL/list) | object |
'curlImport' | cURL import |
IExecuteFunctions Key Methods
| Method | Signature | Purpose |
|---|
getInputData | (inputIndex?: number) => INodeExecutionData[] | Get input items |
getNodeParameter | (name: string, itemIndex: number) => any | Read parameter value |
getCredentials | (type: string) => Promise<ICredentialDataDecryptedObject> | Get decrypted credentials |
continueOnFail | () => boolean | Check continue-on-fail setting |
executeWorkflow | (workflowInfo, inputData?) => Promise<ExecuteWorkflowData> | Call sub-workflow |
putExecutionToWait | (waitTill: Date) => Promise<void> | Pause execution until date |
sendMessageToUI | (message: any) => void | Send message to editor UI |
helpers.httpRequest | (options) => Promise<any> | Make HTTP requests |
helpers.getBinaryDataBuffer | (itemIndex, propertyName) => Promise<Buffer> | Read binary data |
helpers.prepareBinaryData | (buffer, fileName?, mimeType?) => Promise<IBinaryData> | Create binary data |
helpers.normalizeItems | (items) => INodeExecutionData[] | Normalize item format |
Critical Warnings
ALWAYS return INodeExecutionData[][] from execute() -- the outer array represents output indices (for multi-output nodes like IF/Switch), the inner array contains items. Single-output nodes return [returnData].
ALWAYS iterate over input items using this.getInputData() and call getNodeParameter(name, i) with the item index i -- parameters can contain expressions that resolve differently per item.
ALWAYS handle continueOnFail() in the catch block of your item loop -- when enabled, push an error item with pairedItem instead of throwing.
NEVER use &str or borrowed types in node parameters -- n8n uses IDataObject (plain objects) for all parameter values. Cast with as string, as number, etc.
NEVER create an execute() method in declarative nodes -- n8n handles HTTP requests automatically based on routing configuration in properties.
NEVER define inputs on trigger nodes -- trigger nodes ALWAYS have inputs: [] (empty array) because they start workflow execution.
NEVER use this binding in the Node base class -- the Node class passes context as a parameter. Use context.getInputData() instead of this.getInputData().
ALWAYS set noDataExpression: true on resource and operation selector properties -- these properties control node behavior and MUST NOT be expression-dependent.
Decision Trees
Which Node Pattern to Use?
Is this a trigger/starting node?
├── YES: Does it respond to HTTP requests?
│ ├── YES → Webhook pattern (webhook() + webhooks config)
│ └── NO: Does it poll an external service?
│ ├── YES → Poll pattern (poll() + polling: true)
│ └── NO → Trigger pattern (trigger() + emit())
└── NO: Is this a simple REST API wrapper?
├── YES → Declarative pattern (routing in properties, NO execute())
└── NO → Programmatic pattern (execute() with custom logic)
Which Property Type to Use?
What data does the user provide?
├── True/false → 'boolean'
├── Free text → 'string' (add rows for textarea, editor for code)
├── A number → 'number' (set minValue/maxValue in typeOptions)
├── One choice from list → 'options'
├── Multiple choices from list → 'multiOptions'
├── A set of optional fields → 'collection'
├── A structured group of fields → 'fixedCollection'
├── Raw JSON → 'json'
├── Date/time → 'dateTime'
├── A color → 'color'
├── Filter conditions → 'filter'
├── Field mapping → 'resourceMapper'
├── Field assignments → 'assignmentCollection'
├── Resource ID/URL/name → 'resourceLocator'
├── Another workflow → 'workflowSelector'
└── Display-only info → 'notice' or 'callout'
Essential Patterns
Pattern 1: Programmatic Node (implements INodeType)
import type {
IExecuteFunctions,
INodeExecutionData,
INodeType,
INodeTypeDescription,
} from 'n8n-workflow';
import { NodeConnectionTypes } from 'n8n-workflow';
export class MyNode implements INodeType {
description: INodeTypeDescription = {
displayName: 'My Node',
name: 'myNode',
icon: 'file:myIcon.svg',
group: ['transform'],
version: 1,
description: 'Processes data',
defaults: { name: 'My Node' },
inputs: [NodeConnectionTypes.Main],
outputs: [NodeConnectionTypes.Main],
properties: [],
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.();
: [] = [];
( i = ; i < items.; i++) {
{
value = .(, i) ;
returnData.({ : { : value } });
} (error) {
(.()) {
returnData.({
: { : (error ). },
: { : i },
});
;
}
error;
}
}
[returnData];
}
}
Pattern 2: Node Base Class (context parameter)
import { Node } from 'n8n-workflow';
import type {
IExecuteFunctions,
INodeExecutionData,
INodeTypeDescription,
} from 'n8n-workflow';
export class MyNode extends Node {
description: INodeTypeDescription = { };
async execute(
context: IExecuteFunctions
): Promise<INodeExecutionData[][]> {
const items = context.getInputData();
const returnData: INodeExecutionData[] = [];
for (let i = 0; i < items.length; i++) {
const value = context.getNodeParameter('myParam', i) as string;
returnData.push({ json: { result: value } });
}
return [returnData];
}
}
Pattern 3: Declarative Node (routing, NO execute)
export class ApiNode implements INodeType {
description: INodeTypeDescription = {
displayName: 'API Node',
name: 'apiNode',
group: ['input'],
version: 1,
description: 'Interacts with an API',
defaults: { name: 'API Node' },
inputs: [NodeConnectionTypes.Main],
outputs: [NodeConnectionTypes.Main],
credentials: [{ name: 'myApi', required: true }],
requestDefaults: {
baseURL: 'https://api.example.com',
headers: { Accept: 'application/json' },
},
properties: [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
options: [
{
name: 'Get Many',
: ,
: ,
: {
: { : , : },
},
},
],
: ,
},
],
};
}
Pattern 4: displayOptions (Conditional Properties)
{
displayName: 'Limit',
name: 'limit',
type: 'number',
default: 50,
typeOptions: { minValue: 1 },
displayOptions: {
show: {
operation: ['getAll'],
returnAll: [false],
},
},
}
{
displayName: 'Advanced Field',
name: 'advancedField',
type: 'string',
default: '',
displayOptions: {
show: {
'@version': [{ _cnd: { gte: 2 } }],
status: [{ _cnd: { not: 'archived' } }],
},
},
}
Pattern 5: Versioned Node
import type { IVersionedNodeType, INodeType } from 'n8n-workflow';
export class MyNodeVersioned implements IVersionedNodeType {
currentVersion = 2;
description = { };
nodeVersions: { [key: number]: INodeType } = {
1: new MyNodeV1(),
2: new MyNodeV2(),
};
getNodeType(version?: number): INodeType {
return this.nodeVersions[version ?? this.currentVersion];
}
}
Pattern 6: methods Object (Dynamic Loading)
methods = {
loadOptions: {
async getUsers(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
const credentials = await this.getCredentials('myApi');
const users = await this.helpers.httpRequest({ });
return users.map((u: any) => ({ name: u.name, value: u.id }));
},
},
listSearch: {
async searchUsers(
this: ILoadOptionsFunctions,
filter?: string,
paginationToken?: string,
): Promise<INodeListSearchResult> {
},
},
credentialTest: {
async testMyApi(this: ICredentialTestFunctions): Promise<INodeCredentialTestResult> {
},
},
: {
(: ): <> {
},
},
};
INodeExecutionData Structure
interface INodeExecutionData {
json: IDataObject;
binary?: IBinaryKeyData;
error?: NodeApiError;
pairedItem?: IPairedItemData | number;
}
interface IBinaryKeyData {
[key: string]: IBinaryData;
}
ALWAYS include a json property on every output item -- it is the only required field. Binary data is ALWAYS secondary.
execute() Return Type
The return type INodeExecutionData[][] is a 2D array:
- Outer array: One entry per output connector (index 0 = first output, index 1 = second output)
- Inner array: Items for that output
return [returnData];
return [trueItems, falseItems];
return [output1Items, output2Items, output3Items];
return [[]];
Reference Links
Official Sources