Creating Custom Nodes
Relevant source files
The following files were used as context for generating this wiki page:
- CONTRIBUTING.md
- packages/@n8n/create-node/README.md
- packages/@n8n/node-cli/README.md
- packages/node-dev/README.md
- packages/nodes-base/AGENTS.md
- packages/@n8n/ai-workflow-builder.ee/src/tools/utils/node-creation.utils.ts
- packages/@n8n/ai-workflow-builder.ee/src/tools/node-details.tool.ts
- packages/nodes-base/nodes/Webhook/Webhook.node.json
- AGENTS.md
Creating custom nodes allows developers to extend n8n's functionality by integrating new services or adding custom logic. The n8n ecosystem provides a suite of command-line tools designed to streamline the entire node development lifecycle, from initial project scaffolding to final publication. This process involves defining a node's properties, implementing its execution logic in TypeScript, and managing its development cycle with specialized scripts.
This guide covers the tools, structure, and best practices for building, testing, and publishing custom community nodes for n8n. While PRs for new nodes to the core repository are generally not accepted unless specifically requested, n8n provides robust support for creating and distributing community nodes via npm.
Sources: CONTRIBUTING.md:162-164, packages/@n8n/create-node/README.md:3
Scaffolding a New Node
The recommended way to start a new custom node is by using the @n8n/create-node package, a scaffolding tool that generates a new project with best practices built-in.
Sources: packages/@n8n/create-node/README.md:3
Quick Start
To create a new node project, run the following command and follow the interactive prompts:
npm create @n8n/node@latest
The CLI will guide you through configuring the node's name, type (e.g., HTTP API), API base URL, and authentication method.
Sources: packages/@n8n/create-node/README.md:7-11
This diagram illustrates the interactive setup process.
Templates
The scaffolding tool provides several templates to start from:
| Template | Description |
|---|---|
declarative/custom | A minimal structure for a declarative node. |
declarative/github-issues | An example integration for GitHub Issues. |
programmatic/example | A full programmatic node with advanced features. |
Sources: packages/@n8n/create-node/README.md:21-25, packages/@n8n/node-cli/README.md:43-43
Project Structure
A generated node project follows a standardized structure that the n8n tooling expects.
my-awesome-api-node/
├── src/
│ ├── nodes/
│ │ └── MyAwesomeApi/
│ │ ├── MyAwesomeApi.node.ts # Main node logic
│ │ └── MyAwesomeApi.node.json # Node metadata (optional)
│ └── credentials/
│ └── MyAwesomeApiAuth.credentials.ts
├── package.json
├── tsconfig.json
└── README.md
Sources: packages/@n8n/create-node/README.md:118-128, packages/@n8n/node-cli/README.md:149-158
package.json Configuration
The package.json file contains a special n8n key that tells n8n where to find the compiled node and credential files.
{
"name": "n8n-nodes-my-awesome-node",
"n8n": {
"n8nNodesApiVersion": 1,
"nodes": [
"dist/nodes/MyAwesomeApi/MyAwesomeApi.node.js"
],
"credentials": [
"dist/credentials/MyAwesomeApiAuth.credentials.js"
]
}
}
Sources: packages/@n8n/create-node/README.md:132-144, packages/@n8n/node-cli/README.md:162-174
Node Anatomy
Every node is a TypeScript class that implements the INodeType interface. This class defines the node's metadata, properties, and execution logic.
Sources: packages/nodes-base/AGENTS.md:5-7, packages/node-dev/README.md:42-45
This class diagram shows the main components of the INodeType interface.
INodeTypeDescription
The description property contains all the metadata that n8n uses to render the node in the UI and understand its behavior.
| Property | Description |
|---|---|
displayName | The human-readable name displayed in the Editor UI. |
name | The internal, camelCase name for the node. |
group | The node group for categorization in the UI (e.g., transform, trigger). |
version | The version of the node. Can be a single number or an array for light versioning. |
description | A summary of the node's function, displayed in the UI. |
defaults | Default name and color for a new node instance. |
inputs | Defines the number and types of input connections (e.g., ['main']). |
outputs | Defines the number and types of output connections (e.g., ['main']). |
properties | An array of objects defining the user-configurable fields in the node panel. |
credentials | An array defining the credentials the node requires access to. |
subtitle | Optional text displayed under the node name in the UI, can be an expression. |
icon | An optional icon to display for the node. |
Sources: packages/node-dev/README.md:124-142, packages/nodes-base/AGENTS.md:5
Node Execution Logic
A node must implement exactly one of the following methods, which determines its type:
execute(): For regular (programmatic) nodes. This method is called once when the workflow executes and is responsible for the node's core logic.trigger(): For generic trigger nodes. This method is called once when the workflow is activated and can trigger workflow runs independently.webhook(): For webhook-based trigger nodes. This method is called whenever the node's webhook URL receives a request.poll(): For polling-based trigger nodes. This method is executed at a defined interval to check for new data. Thepolling: trueflag must also be set in the node description.
Sources: packages/nodes-base/AGENTS.md:8-13, packages/node-dev/README.md:82-109
Node Properties
The properties array in the node description defines the fields available to the user in the node's configuration panel. Each property is an object with several attributes.
| Attribute | Description |
|---|---|
displayName | The label for the property field in the UI. |
name | The internal, camelCase name used to access the property's value. |
type | The data type of the property (e.g., string, number, boolean, options, collection). |
default | The default value for the property. |
description | A description of the property, often shown as a tooltip. |
required | A boolean indicating if the property must have a value. |
placeholder | Placeholder text for input fields. |
displayOptions | Defines conditional logic to show or hide the property based on other property values. |
typeOptions | Provides additional configuration based on the type (e.g., loadOptionsMethod for dynamic options). |
Sources: packages/node-dev/README.md:144-156, packages/nodes-base/AGENTS.md:28-36
Development Workflow
The @n8n/node-cli package provides scripts to manage the entire development lifecycle. These are typically run via npm scripts configured in the scaffolded package.json.
Sources: packages/@n8n/create-node/README.md:58-61, packages/@n8n/node-cli/README.md:17-19
This flowchart shows the typical development cycle for a custom node.
Key Commands
| Command | n8n-node-cli equivalent | Description |
|---|---|---|
npm run dev | n8n-node dev | Starts a local n8n instance with hot-reloading for your node. It links your node to ~/.n8n-node-cli/.n8n/custom. |
npm run build | n8n-node build | Compiles the TypeScript source code into JavaScript in the dist/ directory, preparing it for distribution. |
npm run lint | n8n-node lint | Lints the code to check for style and convention issues. --fix can be used to auto-fix problems. |
npm run release | n8n-node release | Manages the entire release process, including versioning, changelog updates, git tagging, and publishing to npm. |
Sources: packages/@n8n/create-node/README.md:63-91, packages/@n8n/node-cli/README.md:59-130
Node Instance Creation
Internally, n8n uses utility functions to create and manage node instances. When a node is added to a workflow, a unique name and ID are generated.
generateUniqueName(baseName, existingNodes): Ensures the node has a unique name within the workflow by appending a number if a conflict exists.createNodeInstance(...): Constructs a new node object (INode) with all required properties, including a unique ID. If the node type requires a webhook, awebhookIdis also generated and assigned.requiresWebhook(nodeType): A helper function that checks the node's description to determine if awebhookIdis necessary.
Sources: packages/@n8n/ai-workflow-builder.ee/src/tools/utils/node-creation.utils.ts:10-19, 52-70, 45-48
Best Practices
Adhering to best practices ensures that nodes are robust, maintainable, and provide a good user experience.
- TypeScript: Avoid using the
anytype. Use specific types orunknownand employ type guards instead of type casting withas. - Error Handling: Use
NodeOperationErrorfor user-facing errors andNodeApiErrorfor API-related issues. Support thecontinueOnFailoption where appropriate. - Code Organization: Keep files organized with kebab-case. Separate large descriptions into their own files and create reusable API request helpers.
- UI/UX: Use clear
displayNameanddescriptionfields. Provide sensible default values and usedisplayOptionsto create a clean, conditional UI.
Sources: packages/nodes-base/AGENTS.md:73-90, AGENTS.md:69-72