Guidance for node development in the nodes-base package.
Every node implements the INodeType interface with:
description: INodeTypeDescription- Node metadata and UI configurationexecute?()- For programmatic nodespoll?()- For polling triggers (setpolling: truein description)trigger?()- For generic triggerswebhook?()- For webhook triggerswebhookMethods?- Webhook lifecycle (checkExists, create, delete)methods?- loadOptions, listSearch, credentialTest, resourceMapping
Use execute function for custom logic. Example: nodes/Discord/v2/DiscordV2.node.ts
Use requestDefaults and routing configuration instead of execute. Example: nodes/Okta/Okta.node.ts
- Webhook triggers: Implement
webhookandwebhookMethods(checkExists, create, delete). Example:nodes/Microsoft/Teams/MicrosoftTeamsTrigger.node.ts - Polling triggers: Set
polling: trueand implementpoll. UsegetWorkflowStaticData('node')to persist state. Example:nodes/Google/Gmail/GmailTrigger.node.ts - Generic triggers: Implement
triggerfunction. Example:nodes/MQTT/MqttTrigger.node.ts
Common parameter types:
string- Text inputoptions- Dropdown (static or dynamic vialoadOptionsMethod)resourceLocator- Select by list, ID, or URLcollection- Key-value pairsfixedCollection- Structured collections
Use displayOptions to show/hide fields based on other parameters. Use noDataExpression: true for resource/operation selectors.
- Light versioning: Use version arrays in description:
version: [3, 3.1, 3.2] - Full versioning: Use
VersionedNodeTypeclass with separate version implementations. Example:nodes/Set/Set.node.ts
Credentials are defined in credentials/ directory and implement ICredentialType:
name- Internal identifierdisplayName- Human-readable nameproperties- Credential fieldsauthenticate- Authentication configuration (generic or custom function)test- Credential test request
Nodes can test credentials via methods.credentialTest.
- Use
vitest-mock-extendedfor mocking interfaces - Use
nockfor HTTP mocking - Mock all external dependencies
- Test happy paths, error handling, edge cases, and binary data
- Use
NodeTestHarnesswith JSON workflow definitions - Mock external APIs with nock
- Use
pnpm testfor running tests. Example:cd packages/nodes-base/ && pnpm test TestFileName
- Create directory:
nodes/YourService/ - Create
YourService.node.tsimplementingINodeType - Add icon SVG files in node directory
- Define credentials in
credentials/if needed - Write tests following testing guidelines
- Register in
package.jsonnodes array if needed
Add loadOptionsMethod to parameter's typeOptions and implement method in methods.loadOptions.
Change parameter type to 'resourceLocator', define modes (list, id, url), add searchListMethod for list mode, add extractValue regex for URL mode.
- Never use
anytype - use proper types orunknown - Avoid type casting with
as- use type guards instead - Define interfaces for API responses
- Use
NodeOperationErrorfor user-facing errors - Use
NodeApiErrorfor API-related errors - Support
continueOnFailoption when appropriate
User input is untrusted. In nodes it arrives mainly through
this.getNodeParameter(...) (and incoming item.json), and a workflow author
controls these values.
Never use an untrusted value as a computed object key in an assignment. A
value such as __proto__, constructor, or prototype pollutes the prototype
chain:
// UNSAFE — `table`/`key` come from this.getNodeParameter(...)
if (acc[table] === undefined) acc[table] = {};
acc[table][key] = value;Route dynamic-key writes through the n8n-workflow helpers, or build the
accumulator as a Map / Object.create(null):
import { setSafeObjectProperty, isSafeObjectProperty } from 'n8n-workflow';
if (isSafeObjectProperty(table) && acc[table] === undefined) {
setSafeObjectProperty(acc, table, {});
}This only applies to dynamic-key writes (grouping/aggregating rows by a
user-chosen column is the common case). Reads like const x = obj[key] are
safe. Reference usage: nodes/Google/GSuiteAdmin/GSuiteAdmin.node.ts.
- Separate operation/field descriptions into separate files
- Create reusable API request helpers in GenericFunctions
- Use kebab-case for files, PascalCase for classes
- Use clear
displayNameanddescriptionfields - Set sensible default values
- Use
displayOptionsto show/hide fields conditionally
- Declarative:
nodes/Okta/Okta.node.ts - Programmatic:
nodes/Discord/v2/DiscordV2.node.ts - Webhook Trigger:
nodes/Microsoft/Teams/MicrosoftTeamsTrigger.node.ts - Polling Trigger:
nodes/Google/Gmail/GmailTrigger.node.ts - Generic Trigger:
nodes/MQTT/MqttTrigger.node.ts - Versioned:
nodes/Set/Set.node.ts