Back to Nodes

@devantage/n8n-custom-nodes-framework

v1.1.0
Last updated Aug 13, 2026

A small framework for building custom n8n nodes with resource/operation routing

5 Weekly Downloads
147 Monthly Downloads

Description

!Logo

n8n Custom Nodes Framework

A small framework to create custom n8n nodes using the resource -> operation -> execute pattern.

What it solves

  • Central registry for resources
  • Automatic composition of node properties
  • Execution routing by resource and operation
  • Uniform error handling with NodeOperationError
  • Simple base for creating multi-resource nodes without repeating boilerplate
  • Usage

    import type { IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
    import { ResourceOperation, Resource, ResourceRegistry, ResourceNode } from '@devantage/n8n-custom-nodes-framework';

    class PingOperation extends ResourceOperation { public readonly name = 'ping'; public readonly displayName = 'Ping'; public readonly description = 'Returns a ping payload'; public readonly properties = [];

    public async execute(this: IExecuteFunctions, itemIndex: number): Promise { return { json: { ok: true }, pairedItem: itemIndex, }; } }

    class HealthResource extends Resource { public constructor() { super('health', 'Health', PingOperation); } }

    const registry = new ResourceRegistry(new HealthResource());

    export class ExampleNode extends ResourceNode { public constructor() { super(registry, { displayName: 'Example', name: 'example', icon: 'file:icon.svg', version: 1, description: 'Example node', credentials: [], }); } }

    HTTP client

    HttpClient wraps httpRequestWithAuthentication with a fixed base URL and
    credential type:

    import { HttpClient } from '@devantage/n8n-custom-nodes-framework';

    const client = new HttpClient({ baseURL: 'https://api.example.com/v2', credentialType: 'exampleApi', });

    // inside an operation's execute() const user = await client.get(this, '/users/1');

    Besides head, get, post, put, patch and delete, it offers graphql,
    which POSTs the body, unwraps the data envelope and throws when the response
    carries errors:

    const response = await client.graphql(this, '/graphql', { query, variables });
    

    Pass a FormData body (with its headers and json: false) to perform a
    multipart upload:

    const response = await client.graphql(this, '/graphql', form, {
      headers: form.getHeaders(),
      json: false,
    });
    

    Test utilities

    The framework also provides some helpers to reduce boilerplate in node tests:

    import { TestUtil } from '@devantage/n8n-custom-nodes-framework';

    const context = TestUtil.createExecuteFunctionsMock({ resource: 'health', operation: 'ping', });

    const result = await TestUtil.executeNode(node, context as IExecuteFunctions);

    Available helpers:

  • TestUtil.createExecuteFunctionsMock(): creates a basic mock of IExecuteFunctions, including the assertBinaryData, getBinaryDataBuffer and httpRequestWithAuthentication helpers
  • TestUtil.createNodeExecutionData(): builds input/output items for tests
  • TestUtil.createBinaryData(): generates a simple IBinaryData for file tests
  • TestUtil.executeNode(): runs node.execute without repeating casts in every test