# How to Extend n8n with Custom Functionality: 3 Proven Methods

> Extend n8n with custom functionality using community nodes, Workflow SDK, or backend modules. Unlock new integrations and server-side logic for advanced automation.

- Repository: [n8n - Workflow Automation/n8n](https://github.com/n8n-io/n8n)
- Tags: how-to-guide
- Published: 2026-02-24

---

**You can extend n8n with custom functionality by creating community nodes for new integrations, using the Workflow SDK to generate workflows programmatically, or building backend modules to add REST endpoints and server-side logic.**

n8n is an open-source workflow automation platform designed for extensibility. Whether you need to connect to a proprietary API or automate workflow deployment from CI pipelines, you can extend n8n with custom functionality through three official extension points. The n8n-io/n8n repository provides scaffolding tools, TypeScript SDKs, and dependency injection frameworks to support these customizations without modifying core code.

## Creating Community Nodes for Custom Integrations

Community nodes are self-contained npm packages that appear in the n8n UI alongside built-in nodes. They allow you to add new integrations, operations, and credential types.

### Scaffold Your Node Package

n8n ships a **node-cli template** that bootstraps a fully-functional community node repository.

```bash
npx @n8n/create-node my-awesome-node

```

This command generates a package containing:

- `src/` – the node implementation ([`MyAwesomeNode.node.ts`](https://github.com/n8n-io/n8n/blob/main/MyAwesomeNode.node.ts)).
- [`package.json`](https://github.com/n8n-io/n8n/blob/main/package.json) – standard npm metadata with entry-point set to `dist`.
- [`README.md`](https://github.com/n8n-io/n8n/blob/main/README.md) – documentation skeleton.

The template source is located at [`packages/@n8n/node-cli/src/template/templates/programmatic/example/template/README.md`](https://github.com/n8n-io/n8n/blob/master/packages/@n8n/node-cli/src/template/templates/programmatic/example/template/README.md).

### Implement the Node Class

A node must export a class implementing `INodeType`. The **description** property defines UI metadata, while the **execute** method contains runtime logic.

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

export class MyAwesomeNode implements INodeType {
    description: INodeTypeDescription = {
        displayName: 'My Awesome',
        name: 'myAwesome',
        group: ['transform'],
        version: 1,
        description: 'Calls the Awesome API',
        defaults: {
            name: 'My Awesome',
        },
        inputs: ['main'],
        outputs: ['main'],
        credentials: [
            {
                name: 'awesomeApi',
                required: true,
            },
        ],
        properties: [
            {
                displayName: 'Action',
                name: 'action',
                type: 'options',
                options: [
                    { name: 'Get Data', value: 'getData' },
                    { name: 'Create Item', value: 'createItem' },
                ],
                default: 'getData',
            },
        ],
    };

    async execute(this: IExecuteFunctions): Promise<any[][]> {
        const action = this.getNodeParameter('action', 0) as string;
        const credentials = await this.getCredentials('awesomeApi');
        const endpoint = `https://api.awesome.com/${action}`;

        const response = await this.helpers.request({
            method: 'GET',
            url: endpoint,
            auth: {
                user: credentials?.username,
                password: credentials?.password,
            },
        });

        return [this.helpers.returnJsonArray(response)];
    }
}

```

The node lives in [`src/MyAwesomeNode.node.ts`](https://github.com/n8n-io/n8n/blob/main/src/MyAwesomeNode.node.ts) and compiles to `dist/` via the package's build script.

### Define Credentials

If your external service requires authentication, define a credentials class in [`src/AwesomeApi.credentials.ts`](https://github.com/n8n-io/n8n/blob/main/src/AwesomeApi.credentials.ts) and reference it in the node's `description.credentials` array. For the exact structure, see existing credentials files such as [[`packages/nodes-base/credentials/ZoomOAuth2Api.credentials.ts`](https://github.com/n8n-io/n8n/blob/main/packages/nodes-base/credentials/ZoomOAuth2Api.credentials.ts)](https://github.com/n8n-io/n8n/blob/master/packages/nodes-base/credentials/ZoomOAuth2Api.credentials.ts).

### Register and Package

[`package.json`](https://github.com/n8n-io/n8n/blob/main/package.json) must expose the entry point for n8n's discovery mechanism:

```json
{
  "name": "n8n-nodes-my-awesome",
  "version": "0.1.0",
  "main": "dist/MyAwesomeNode.node.js",
  "n8n": {
    "nodes": ["dist/MyAwesomeNode.node.js"]
  },
  "scripts": {
    "build": "tsc -p tsconfig.json"
  },
  "dependencies": {
    "n8n-workflow": "^0.229.0"
  }
}

```

When installed globally or in a custom n8n instance, n8n automatically loads nodes listed under the `n8n.nodes` field.

### Test Your Implementation

n8n provides a Jest-based test harness. Add a test file at [`src/__tests__/MyAwesomeNode.test.ts`](https://github.com/n8n-io/n8n/blob/main/src/__tests__/MyAwesomeNode.test.ts):

```typescript
import { MyAwesomeNode } from '../MyAwesomeNode.node';
import { IExecuteFunctions } from 'n8n-workflow';

test('execute calls API with correct endpoint', async () => {
    const node = new MyAwesomeNode();
    const getNodeParameter = jest.fn().mockReturnValue('getData');
    const helpers = {
        request: jest.fn().mockResolvedValue({ success: true }),
        returnJsonArray: (data: any) => [data],
    };
    const context = { getNodeParameter, helpers } as unknown as IExecuteFunctions;

    const result = await node.execute.call(context);
    expect(helpers.request).toHaveBeenCalledWith(
        expect.objectContaining({ url: expect.stringContaining('getData') })
    );
    expect(result).toEqual([[{ success: true }]]);
});

```

Run `pnpm test` inside the generated node folder to verify. The test harness configuration is available at [[`packages/nodes-base/test/setup.ts`](https://github.com/n8n-io/n8n/blob/main/packages/nodes-base/test/setup.ts)](https://github.com/n8n-io/n8n/blob/master/packages/nodes-base/test/setup.ts).

### Publish and Install

```bash
npm publish
npm install -g n8n-nodes-my-awesome

```

Once installed, the node appears in the UI under the **Transform** group.

## Using the Workflow SDK for Programmatic Automation

The **Workflow SDK** in `packages/@n8n/workflow-sdk` provides a type-safe builder API for generating workflows from code.

### Install the Workflow SDK

```bash
npm install @n8n/workflow-sdk

```

### Build Workflows in Code

Use the fluent builder API to construct workflows dynamically:

```typescript
import {
    WorkflowBuilder,
    manual,
    httpRequest,
    ifNode,
    set,
} from '@n8n/workflow-sdk';

const wf = new WorkflowBuilder()
    .withName('Sample API Trigger')
    .addTrigger(
        manual()
    )
    .then(
        httpRequest({ url: 'https://api.example.com/items' })
    )
    .then(
        ifNode()
            .condition('{{ $json.status === "active" }}')
            .ifTrue(set({ data: '{{ $json }}' }))
            .ifFalse(set({ data: { message: 'inactive' } }))
    )
    .build();

import { writeFileSync } from 'fs';
writeFileSync('sample-workflow.json', JSON.stringify(wf, null, 2));

```

Reference the SDK documentation at [`packages/@n8n/workflow-sdk/README.md`](https://github.com/n8n-io/n8n/blob/master/packages/@n8n/workflow-sdk/README.md).

### Deploy via REST API

Import the generated JSON into n8n programmatically:

```bash
curl -X POST \
  -H "Content-Type: application/json" \
  -d @sample-workflow.json \
  http://localhost:5678/rest/workflows

```

The API accepts the payload, stores the workflow, and makes it executable from the UI or via the CLI (`n8n execute`).

## Adding Backend Modules for Server-Side Logic

Backend modules let you add **new REST endpoints**, services, repositories, or scheduled jobs without touching core n8n code. They use the dependency injection system defined in [`packages/@n8n/di`](https://github.com/n8n-io/n8n/blob/master/packages/@n8n/di/README.md).

### Scaffold a Backend Module

```bash
pnpm dlx @n8n/create-node backend-module myFeature

```

The generated folder contains:

- [`myFeature.module.ts`](https://github.com/n8n-io/n8n/blob/main/myFeature.module.ts) – registers providers.
- [`myFeature.service.ts`](https://github.com/n8n-io/n8n/blob/main/myFeature.service.ts) – business logic.
- [`myFeature.controller.ts`](https://github.com/n8n-io/n8n/blob/main/myFeature.controller.ts) – Express route definitions.
- [`myFeature.repository.ts`](https://github.com/n8n-io/n8n/blob/main/myFeature.repository.ts) – optional TypeORM data access.

### Create Custom Controllers

Define REST endpoints using decorators:

```typescript
import { Controller, Get, Param } from '@n8n/di';
import { MyFeatureService } from './myFeature.service';

@Controller('/my-feature')
export class MyFeatureController {
    constructor(private readonly service: MyFeatureService) {}

    @Get('/:id')
    async get(@Param('id') id: string) {
        return this.service.fetchData(id);
    }
}

```

### Register the Module

Add the module to the central DI bootstrap in [`src/modules.ts`](https://github.com/n8n-io/n8n/blob/main/src/modules.ts):

```typescript
import { MyFeatureModule } from './my-feature/myFeature.module';

export const modules = [
    MyFeatureModule,
];

```

After rebuilding (`pnpm build`) and restarting (`pnpm start`), the new endpoint is available at `GET http://localhost:5678/my-feature/123`.

Detailed guidance is available in [[`scripts/backend-module/backend-module-guide.md`](https://github.com/n8n-io/n8n/blob/main/scripts/backend-module/backend-module-guide.md)](https://github.com/n8n-io/n8n/blob/master/scripts/backend-module/backend-module-guide.md).

## Summary

- **Community nodes** provide UI-visible integrations for external services and require implementing `INodeType` with a description and execute method.
- **Workflow SDK** enables type-safe, programmatic workflow generation using `WorkflowBuilder` for CI/CD pipelines or dynamic automation.
- **Backend modules** extend the n8n server with custom REST endpoints and business logic using the `@n8n/di` dependency injection framework.
- All extension points use official scaffolding tools (`@n8n/create-node`) to maintain compatibility with the n8n-io/n8n codebase.

## Frequently Asked Questions

### Can I extend n8n without modifying core source code?

Yes. All three extension methods—community nodes, the Workflow SDK, and backend modules—allow you to add functionality while treating n8n as a dependency. You install community nodes via npm, import the SDK as a library, or register backend modules through the official plugin system.

### What is the difference between community nodes and backend modules?

**Community nodes** add new workflow steps that appear in the visual editor for non-technical users. **Backend modules** add server-side capabilities like custom REST endpoints or database repositories, invisible to the UI but accessible via API. Choose community nodes for integrations and backend modules for infrastructure extensions.

### How do I test custom n8n nodes locally?

Use the Jest test harness provided in the `n8n-nodes-base` package. Mock the `IExecuteFunctions` context, including `getNodeParameter` and `helpers.request`, then invoke the node's `execute` method directly. Run `pnpm test` within your node package directory.

### Can I programmatically create and execute workflows without the UI?

Yes. The Workflow SDK (`@n8n/workflow-sdk`) allows you to build workflow JSON definitions in TypeScript. You can then import these via the REST API at `POST /rest/workflows` or execute them immediately using the n8n CLI command `n8n execute --file workflow.json`.