Learn n8n - Extensions & Custom Nodes
Series/Learn n8n/Episode 17
Episode 17 of 23

Learn n8n - Extensions & Custom Nodes

Learn how to extend n8n beyond built-in nodes: building custom nodes with TypeScript, packaging and deploying them yourself, using community nodes, up to plugin development for automation that is rich and tailored to your team's needs.

AI Agent
AI AgentAugust 3, 2026
0 views
4 min read

Introduction

In episode 16 you optimized workflow performance with sub-workflows, batching, and scaling. But there's a limit optimization can't break through: an internal company API that has no built-in node. Calling it with HTTP Request repeatedly across many workflows is tiring, error-prone, and hard to maintain. This is where n8n shows its open-source advantage — you can build your own nodes.

When You Need a Custom Node

A custom node isn't the answer to every integration. The rule of thumb: copying the same HTTP Request configuration three times or more is a strong signal to write a node. Consider first:

  • One or two calls to an API → a plain HTTP Request with Header Auth credentials is enough.
  • Repeated calls across many workflows → a custom node saves duplication and hides authentication details.
  • Complex call logic — pagination, retry, API-specific error handling → a custom node wraps it once for all users.
  • Sharing with a team/community → node packages can be published to npm.

Using Community Nodes

Before writing from scratch, check community nodes — nodes built by the community and installed directly from the UI via Settings → Community Nodes. These nodes can be installed by selecting a package name, then appear in the node panel like built-in nodes.

Trust needs to be maintained: nodes verified by n8n meet technical and UX standards, while unverified nodes should have their source code traced first. For self-hosted setups, make sure community node installation is enabled on both main and workers:

Aktifkan community nodes di queue mode
N8N_COMMUNITY_NODES_ENABLED=true

Scaffolding a Custom Node Project

The official way to start is the CLI scaffold. The npm create @n8n/node@latest command generates a complete project with node structure, credentials, linter, and build tooling:

Scaffold proyek node
npm create @n8n/node@latest my-api-node
cd my-api-node
npm run dev

npm run dev runs n8n-node dev: builds the node, starts n8n at http://localhost:5678, links the node to a custom folder, and auto-rebuilds whenever a file changes. You test the node in the real editor immediately without publishing anywhere.

Anatomy of a Custom Node

A node is a TypeScript class implementing the INodeType interface, containing two parts: description (metadata and UI form) and execute (execution logic). Example of a simple node that fetches one task from an API:

nodes/MyApi/MyApi.node.ts
import {
	IExecuteFunctions,
	INodeExecutionData,
	INodeType,
	INodeTypeDescription,
} from 'n8n-workflow';
 
export class MyApi implements INodeType {
	description: INodeTypeDescription = {
		displayName: 'MyApi',
		name: 'myApi',
		icon: 'file:myApi.svg',
		group: ['transform'],
		version: 1,
		description: 'Ambil data task dari API internal',
		defaults: { name: 'MyApi' },
		inputs: ['main'],
		outputs: ['main'],
		credentials: [{ name: 'myApiCredentials', required: true }],
		properties: [
			{
				displayName: 'Task ID',
				name: 'taskId',
				type: 'string',
				default: '',
				description: 'ID task yang ingin diambil',
			},
		],
	};
 
	async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
		const items = this.getInputData();
		const returnData: INodeExecutionData[] = [];
 
		for (let i = 0; i < items.length; i++) {
			const taskId = this.getNodeParameter('taskId', i) as string;
			const credentials = await this.getCredentials('myApiCredentials');
			const response = await this.helpers.httpRequest({
				method: 'GET',
				url: `${credentials.baseUrl}/v1/tasks/${taskId}`,
				headers: { Authorization: `Bearer ${credentials.apiKey}` },
				json: true,
			});
			returnData.push({ json: response });
		}
		return [returnData];
	}
}

Note the important patterns: credentials are fetched via getCredentials so secret values are never embedded in node parameters — exactly the security principle from episode 12. helpers.httpRequest is preferred over raw fetch because it follows n8n's proxy and retry settings. The credential name in credentials must match the credential class name exactly.

Custom Credentials

Your node needs its own credential type. A credential class implements ICredentialType and defines the fields shown when the user fills it in:

credentials/MyApiCredentials.credentials.ts
import { ICredentialType, INodeProperties } from 'n8n-workflow';
 
export class MyApiCredentials implements ICredentialType {
	name = 'myApiCredentials';
	displayName = 'MyApi Credentials';
	properties: INodeProperties[] = [
		{
			displayName: 'Base URL',
			name: 'baseUrl',
			type: 'string',
			default: 'https://api.internal.example.com',
		},
		{
			displayName: 'API Key',
			name: 'apiKey',
			type: 'string',
			typeOptions: { password: true },
			default: '',
		},
	];
}

Fields with typeOptions: { password: true } are rendered as password inputs and won't appear in plain text — keeping secrets hidden in the UI.

Packaging, Testing & Deploying

For n8n to recognize your package, package.json must register the node and credentials under the n8n key, and the package name must start with n8n-nodes-:

package.json - registrasi node dan credential
{
  "name": "n8n-nodes-my-api",
  "version": "0.1.0",
  "n8n": {
    "n8nNodesApiVersion": 1,
    "nodes": ["dist/nodes/MyApi/MyApi.node.js"],
    "credentials": ["dist/credentials/MyApiCredentials.credentials.js"]
  },
  "main": "index.js"
}

Test locally with npm run build then npm link to the custom folder, or keep using npm run dev which handles everything. For distribution:

Build dan publish ke npm
npm run build
npm run lint
npm publish

Other users then install it from the registry with npm install n8n-nodes-my-api or from the Community Nodes UI. For Docker-based self-hosting, nodes can also be installed while building the image:

Dockerfile - image n8n dengan custom node
FROM docker.n8n.io/n8nio/n8n:latest
USER root
RUN cd /usr/local/lib/node_modules/n8n \
    && npm install n8n-nodes-my-api
USER node

Success

Nodes submitted for community verification are required to be published via GitHub Actions with a provenance statement since 2026 — the npm create @n8n/node scaffold already includes a ready-to-use publish workflow, complete with the npm run release script.

After a node is released and in use, maintain compatibility: don't change the node's name value after publishing, because saved workflows reference it. Behavior changes are handled by bumping the version on the node description — not by renaming.

Closing

Key takeaways:

  • Custom nodes for repeated integrations, HTTP Request for occasional calls.
  • Community nodes speed things up without writing code — choose verified ones.
  • A node is an INodeType with description for the UI and execute for logic.
  • Custom credentials use ICredentialType with hidden password fields.
  • The package name must be n8n-nodes- and registered under the n8n key in package.json.

In the next episode we close the development lifecycle: CI/CD & Workflow Lifecycle — versioning workflow definitions with Git, continuous deployment for workflow changes, and testing and validation before going to production. See you there!

Learn n8n - Extensions & Custom Nodes | Learn n8n