2026-06-07

Building Custom n8n Nodes: Create Your Own Integration in 30 Minutes

Complete guide to building custom n8n nodes. From the n8n node dev CLI to publishing on npm. Covers: node structure, credential management, API integration, TypeScript patterns, testing, and publishing. Build integrations for any internal or public API.

Building Custom n8n Nodes: Create Your Own Integration in 30 Minutes

n8n has 400+ native nodes. But your company has internal APIs, your favorite SaaS tool doesn't have a node yet, or you need a custom integration nobody else would build. That's when you build your own node.

When to Build a Custom Node

  • Your company has an internal API used across multiple workflows
  • A SaaS tool you love has no n8n node (but has a REST API)
  • You need specialized functionality (custom encryption, proprietary protocol)
  • You want to contribute back to n8n's open-source ecosystem

For one-off API calls in a single workflow, use the HTTP Request node. Build a custom node when you'll reuse it across many workflows.

Quick Start: n8n Node Dev CLI

n8n provides a scaffolding tool:

```bash npm install -g n8n-node-dev n8n-node-dev new ```

This prompts for:

  • Node name (e.g., "AcmeCRM")
  • Description
  • Package name (npm-compatible)

It generates:

``` acme-crm/ ├── nodes/ │ └── AcmeCRM/ │ ├── AcmeCRM.node.ts # Main node logic │ ├── AcmeCRM.node.json # Node metadata │ └── AcmeCRMApi.ts # API wrapper ├── credentials/ │ └── AcmeCRMApi.credentials.ts # Auth handling └── package.json ```

Node Structure

The Node JSON (Metadata)

```json { "node": "n8n-nodes-base.acmeCrm", "nodeVersion": "1.0", "codexVersion": "1.0", "categories": ["Communication"], "resources": { "credentialDocumentation": [{"url": "https://docs.acmecrm.com/api"}], "primaryDocumentation": [{"url": "https://docs.acmecrm.com/api"}] } } ```

The Node TypeScript (Logic)

```typescript import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';

export class AcmeCrm implements INodeType { description: INodeTypeDescription = { displayName: 'Acme CRM', name: 'acmeCrm', group: ['transform'], version: 1, description: 'Interact with Acme CRM API', defaults: { name: 'Acme CRM' }, inputs: ['main'], outputs: ['main'], credentials: [{ name: 'acmeCrmApi', required: true }], properties: [ // Operation selector { displayName: 'Operation', name: 'operation', type: 'options', options: [ { name: 'Create Contact', value: 'createContact' }, { name: 'Get Contact', value: 'getContact' }, { name: 'List Contacts', value: 'listContacts' }, ], default: 'createContact', }, // Fields (shown based on operation) { displayName: 'Email', name: 'email', type: 'string', default: '', required: true, displayOptions: { show: { operation: ['createContact'] } }, }, { displayName: 'Name', name: 'name', type: 'string', default: '', displayOptions: { show: { operation: ['createContact'] } }, }, ], };

async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> { const items = this.getInputData(); const returnData: INodeExecutionData[] = []; const credentials = await this.getCredentials('acmeCrmApi');

for (let i = 0; i < items.length; i++) {
  const operation = this.getNodeParameter('operation', i) as string;

  if (operation === 'createContact') {
    const email = this.getNodeParameter('email', i) as string;
    const name = this.getNodeParameter('name', i) as string;

    const response = await this.helpers.httpRequest({
      method: 'POST',
      url: 'https://api.acmecrm.com/v1/contacts',
      headers: {
        Authorization: `Bearer ${credentials.apiKey}`,
        'Content-Type': 'application/json',
      },
      body: { email, name },
    });

    returnData.push({ json: response });
  }
}

return [returnData];

} } ```

The Credentials File

```typescript import { ICredentialType, INodeProperties } from 'n8n-workflow';

export class AcmeCrmApi implements ICredentialType { name = 'acmeCrmApi'; displayName = 'Acme CRM API'; properties: INodeProperties[] = [ { displayName: 'API Key', name: 'apiKey', type: 'string', default: '', typeOptions: { password: true }, // Masks the field }, ]; } ```

Installing Your Custom Node

Method 1: NPM Package (Recommended for Teams)

```bash

In your n8n directory

npm install @yourcompany/n8n-nodes-acme-crm ```

Set the environment variable:

```yaml environment:

  • N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/node_modules/@yourcompany ```

Method 2: Local Development

```yaml volumes:

  • ~/.n8n/custom-nodes:/home/node/.n8n/custom environment:
  • N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom ```

Testing Custom Nodes

n8n-node-dev includes a test runner:

```typescript // AcmeCRM.test.ts import { testWorkflow } from 'n8n-node-dev';

const workflow = { nodes: [ { id: 'manual-1', name: 'Manual', type: 'n8n-nodes-base.manualTrigger', position: [250, 300] }, { id: 'acme-1', name: 'Acme CRM', type: 'n8n-nodes-base.acmeCrm', position: [450, 300], parameters: { operation: 'createContact', email: 'test@example.com', name: 'Test User' } } ], connections: { 'Manual': { main: [[{ node: 'Acme CRM', type: 'main', index: 0 }]] } } };

testWorkflow(workflow, { credentials: { acmeCrmApi: { apiKey: 'test-key' } } }); ```

Publishing to npm

```bash npm login npm publish --access public ```

n8n's community node registry picks it up automatically. Others can install and use your node.

When NOT to Build a Custom Node

  • One-off API call → Use the HTTP Request node
  • Rapidly changing API → HTTP Request node is easier to update than rebuilding a node
  • Simple webhook → Generic Webhook node works

Build custom nodes for reuse, not one-offs. The HTTP Request node handles the 80% case.

Start with FlowForge templates to see how professional workflows are structured — then extend with custom nodes where needed. Browse templates →

Related n8n Templates

These pre-built n8n templates complement what you just read. Import and run in minutes.

Related Articles

More in-depth guides and comparisons to level up your n8n skills.

Ready to automate?

Browse 25+ production-ready n8n templates. Import, configure, and run — all in under 10 minutes.

Browse Templates