This page covers the development lifecycle for custom n8n nodes, including the CLI tooling for scaffolding, testing, and publishing nodes as community packages. For details on the node type system and registration mechanism, see Node Type System and Registration. For information about the built-in nodes, see Standard Nodes and Integrations and AI and LangChain Nodes.
n8n supports custom node development through two distribution models:
n8n-nodes-base packagen8n-nodes-<name> naming conventionThe development ecosystem provides CLI tools for scaffolding, linting, and building custom nodes, along with automated validation to ensure community packages meet quality standards before installation.
Sources: packages/@n8n/create-node/package.json1-70 packages/@n8n/node-cli/package.json1-60 packages/@n8n/eslint-plugin-community-nodes/package.json1-95
The n8n node development ecosystem consists of these primary tools:
| Package | Purpose | Main Command |
|---|---|---|
n8n-node-dev (packages/node-dev) | Interactive scaffolding for new nodes and credentials | n8n-node-dev new |
@n8n/eslint-plugin-community-nodes | ESLint rules for community node standards | Used via ESLint config |
The n8n-node-dev package provides an interactive CLI using @oclif/core that walks developers through creating a new node or credential file from a template.
Scaffolding workflow ā packages/node-dev New command
Sources: packages/node-dev/commands/new.ts1-156 packages/node-dev/src/Create.ts1-32
n8n-node-dev new InternalsThe New class extends Command from @oclif/core. It uses inquirer for prompts and the change-case library to derive three casing variants of the user-supplied name:
| Template Variable | Transformation | Example |
|---|---|---|
ClassNameReplace | changeCase.pascalCase | MyNode |
DisplayNameReplace | changeCase.capitalCase | My Node |
N8nNameReplace | changeCase.camelCase | myNode |
NodeDescriptionReplace | Raw string | Does something useful |
The createTemplate function in packages/node-dev/src/Create.ts copies the chosen template file to the destination path and then runs replaceInFile to substitute all four variables with the user-supplied values.
Sources: packages/node-dev/commands/new.ts96-143 packages/node-dev/src/Create.ts1-32
The @n8n/eslint-plugin-community-nodes package enforces community node standards using @typescript-eslint/utils for AST analysis. It uses fastest-levenshtein for string-similarity checks (typo detection in node/credential names).
Key rule categories:
| Rule Category | Description |
|---|---|
| Naming conventions | Node/credential class names, n8n name property format |
| Node properties | Required fields in INodeTypeDescription (displayName, name, group, version) |
| Credential rules | OAuth2 implementation standards |
| File structure | Nodes under nodes/, credentials under credentials/ |
Sources: packages/node-dev/commands/new.ts1-20
A community node package must follow this structure:
n8n-nodes-mypackage/
āāā package.json # NPM package manifest with n8n section
āāā nodes/
ā āāā MyNode/
ā ā āāā MyNode.node.ts # INodeType implementation
ā ā āāā icon.svg # Node icon
ā āāā MyTrigger/
ā āāā MyTrigger.node.ts
āāā credentials/
ā āāā MyApi.credentials.ts # ICredentialType implementation
āāā dist/ # Compiled JavaScript output
ā āāā nodes/
ā āāā credentials/
āāā tsconfig.json # TypeScript configuration
The package.json must include an n8n metadata section listing all nodes and credentials:
This structure mirrors the built-in packages like n8n-nodes-base:
Sources: packages/nodes-base/package.json1-50 packages/@n8n/nodes-langchain/package.json1-50
A node implementation must implement the INodeType interface from n8n-workflow:
Sources: packages/workflow/package.json1-74
Nodes support multiple versions through the version property:
Version arrays like [3, 3.1] indicate minor version increments within the same major version.
Sources: packages/nodes-base/package.json412-900
The @n8n/eslint-plugin-community-nodes package enforces community node standards:
The plugin uses @typescript-eslint/utils to analyze node implementations and suggest corrections using Levenshtein distance for typos.
Sources: packages/@n8n/eslint-plugin-community-nodes/package.json1-95
Key rules enforced by the plugin:
| Rule Category | Description | Example |
|---|---|---|
| Naming | Node/credential naming conventions | communityPackageName must match n8n-nodes-* |
| Node Properties | Required properties in INodeTypeDescription | displayName, name, group, version |
| Credentials | OAuth2 implementation standards | Required OAuth2 properties |
| File Structure | Expected file locations | Nodes in nodes/, credentials in credentials/ |
Sources: packages/@n8n/eslint-plugin-community-nodes/package.json1-95
Community packages typically use TypeScript compilation with tsc:
The n8n-core package provides utilities for post-build processing:
| Utility | Purpose | Usage |
|---|---|---|
n8n-copy-static-files | Copy icons and assets | n8n-copy-static-files |
n8n-generate-metadata | Generate node metadata | n8n-generate-metadata |
n8n-generate-translations | Extract i18n strings | n8n-generate-translations |
Sources: packages/core/package.json1-82
To test a community package locally:
Sources: packages/cli/package.json1-196
Before publishing, ensure:
n8n-nodes- or @scope/n8n-nodes-1.0.0)package.jsonn8n, n8n-community-node-packageSources: packages/nodes-base/package.json1-50
n8n provides REST API endpoints for managing community packages through CommunityPackagesController at /rest/community-packages:
Community package install/update/remove lifecycle
Sources: packages/node-dev/commands/new.ts1-5
Before installation, n8n validates community packages:
| Check | Validation |
|---|---|
| Package name | Must match n8n-nodes-* pattern |
| n8n version | Must specify compatible n8n version |
| Node structure | Must have valid n8n section in package.json |
| Security | Scanned for known vulnerabilities |
| Size | Package size limits enforced |
Community packages are installed in:
~/.n8n/nodes/data/nodesN8N_CUSTOM_EXTENSIONS environment variableSources: packages/cli/package.json93-194
Key security practices:
Sources: packages/node-dev/commands/new.ts1-156
n8n includes a ThirdPartyLicensesController that exposes the bundled THIRD_PARTY_LICENSES.md file over HTTP. This file is generated at build time and lists the licenses of all npm dependencies bundled into the packages/cli distribution.
ThirdPartyLicensesController endpoint
@RestController('/third-party-licenses') and @Get('/') from @n8n/decorators.401).Content-Type: text/markdown; charset=utf-8 on success.404 if the file cannot be read (missing or permission error).getThirdPartyLicenses() in packages/frontend/@n8n/rest-api-client/src/api/third-party-licenses.ts.Sources: packages/cli/src/controllers/third-party-licenses.controller.ts1-25 packages/frontend/@n8n/rest-api-client/src/api/third-party-licenses.ts1-10 packages/cli/test/integration/controllers/third-party-licenses.controller.test.ts1-96
Community packages can depend on external libraries:
The pnpm workspace catalog defines standard versions for common dependencies:
Sources: pnpm-lock.yaml1-277
Nodes can use multiple authentication strategies:
| Credential Type | Use Case | Example |
|---|---|---|
HttpBasicAuth | Basic authentication | Username/password |
OAuth2Api | OAuth2 flow | Google, GitHub |
ApiKeyAuth | API key | Header or query param |
JwtAuth | JWT tokens | Short-lived tokens |
For detailed credential implementation, see Credential System.
Sources: packages/nodes-base/package.json24-411
The IExecuteFunctions interface provides access to:
Sources: packages/node-dev/src/Create.ts1-32
Nodes can load parameter options dynamically:
This pattern is extensively used in built-in nodes for dynamic dropdowns.
Refresh this wiki
This wiki was recently refreshed. Please wait 2 days to refresh again.