Сегодня многие разработчики различного уровня знают n8n как гибкий инструмент для автоматизации. В нем можно собрать почти любой сценарий - от простейшего Telegram-бота до сложных бизнес-процессов, используя множество готовых нод (узлов) для работы с популярными сервисами.

Но что делать, если в это множество не входит один из используемых вами сервисов, а через ноду HTTP Requests работать крайне сложно и неудобно? Или, может, хочется подключить собственный API и работать с ним по собственной логике?

Здесь напрашивается довольно простой ответ: написать свою ноду на n8n. Изначально это кажется сложным, однако, прочитав эту статью, вы сможете буквально за 1 вечер собрать минимально-рабочую ноду под ваши "хотелки".

Community nodes - что это?

Что очевидно из названия, это нода, написанная каким-то разработчиком из комьюнити (сообщества) n8n.

Комьюнити-ноды недоступны в списке нод в воркфлоу без предварительной их установки. Все ноды, которые доступны без дополнительных манипуляций, проверены и добавлены именно разработчиками n8n.

Такие ноды публикуются разработчиками как пакет в npm Registry, название которого обязательно начинается с n8n-nodes для того, чтобы n8n смог увидеть его.

Например: n8n-nodes-amvera-inference или n8n-nodes-my-first-node

Где можно развернуть n8n

Перед тем как писать свою ноду, нужно подготовить среду. n8n можно установить локально, в Docker, развернуть в облаке или на VDS/VPS.

В моём случае всё происходило в Amvera Cloud - сервисе для простого и быстрого развертывания IT-приложений, в котором n8n уже есть как настроенный сервис с бесплатным доменом, запуском "одной кнопкой" и собственной нодой доступа к LLM c оплатой в рублях.

Установка комьюнити-ноды

Первым делом необходимо убедиться, что ваша версия n8n и окружение поддерживает установку комьюнити-нод.

Если вы используете преднастроенный сервис в Amvera, дополнительно ничего настраивать не нужно и установка community nodes должна быть доступна в настройках n8n.

Настройки n8n
Настройки n8n
Community nodes
Community nodes

Если же таких нет - попробуйте обновить n8n или создать/изменить переменную окружения N8N_COMMUNITY_NODES_ENABLED в значении true.

Здесь можно установить любую комьюнити-ноду из списка.

  • Если при установке комьюнити-ноды n8n возвращает ошибку:

  • Error installing new package: Request failed with status 504,

  • Попробуйте установить ноду еще раз через несколько минут, это нормально.

Создаем свою ноду шаг за шагом

Итак, когда у нас готово все окружение, мы можем приступать к практической части.

Мы разберем нашу ноду n8n-nodes-amvera-inference, которая упрощает работу с Amvera LLM Inference API, отправляя запросы к API с определенным токеном и возвращая ответ в двух видах: RAW JSON и Ответ модели (только сам ответ).

Подготовка проекта

Важно понимать, что нода - обычный npm-пакет, но с особой структурой.

Официальный репозиторий для быстрого старта доступен на GitHub: https://github.com/n8n-io/n8n-nodes-starter

Структура проекта

Структура нашей ноды выглядит следующим образом:

n8n-nodes-amvera-inference
├── credentials
│   └── AmveraLlmApi.credentials.ts
├── index.ts
├── nodes
│   └── AmveraLlm.node.ts
├── package.json
└── tsconfig.json
  • package.json - можно полностью скопировать с официального репозитория n8n-nodes-starter. Важно изменить название пакета.

  • tsconfig.json - для предложенной структуры выше будет выглядеть так:

 {
  "compilerOptions": {
    "target": "ES2019",
    "module": "commonjs",
    "declaration": true,
    "outDir": "dist",
    "rootDir": ".",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "moduleResolution": "node",
    "resolveJsonModule": true
  },
  "include": ["nodes/**/*.ts", "credentials/**/*.ts"],
  "exclude": ["node_modules", "dist"]
}
  • AmveraLlmApi.credentials.ts - содержит в себе описание Credentials, используемых для ноды, если она требует авторизацию на TypeScript.

  • AmveraLlm.node.ts - основной код (логика) работы ноды. Чуть ниже будет его разбор на рабочем примере.

  • index.ts - необязательный файл, содержащий в себе export всех модулей.

AmveraLlmApi.credentials.ts

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

export class AmveraLlmApi implements ICredentialType {
    name = 'amveraLlmApi';
    displayName = 'Amvera LLM API';
    properties: INodeProperties[] = [
        {
            displayName: 'API Token', // имя, которое будет отображаться в n8n
            name: 'apiToken', // имя, по которому мы будем общаться дальше в коде
            type: 'string', // тип кредов
            typeOptions: { password: true }, // доп. настройки
            default: '', // значение по дефолту - пустое
            required: true, // обязательно ли наличие кредов
            description: 'Токен от модели LLM', // описание в UI n8n
        },
    ];
}

Как и говорилось ранее - этот файл содержит в себе описание вида Credentials для того, чтобы n8n понимал, как формировать окно создания кредов.

AmveraLlm.node.ts

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

export class AmveraLlm implements INodeType {
    description: INodeTypeDescription = {
        displayName: 'Amvera LLM', // отображаемое имя
        name: 'amveraLlm', // имя для обращений в коде
        group: ['transform'], // группа (тип) ноды
        version: 1, // версия
        description: 'Работа с Amvera LLM API', // описание
        defaults: { name: 'Amvera LLM' }, // дефолтные значения
        inputs: ['main'], // входные данные (из предыдущей ноды)
        outputs: ['main'], // выходные данные (в следующую ноду)
        credentials: [{ name: 'amveraLlmApi', required: true }], // какие креды используются. Здесь мы как раз пишем имя из созданного ранее файла.
        properties: [ // Описание полей внутри ноды. У нас это: выбор модели, создание сообщений для LLM, выбор режима вывода
            {
                displayName: 'Model', // отображаемое имя настройки ноды
                name: 'model', // внутреннее имя настройки ноды
                type: 'options', // тип (options - выпадающий список)
                options: [ // что можно выбрать {name, value}
                    { name: 'llama8b', value: 'llama8b' },
                    { name: 'llama70b', value: 'llama70b' },
                    { name: 'gpt-4.1', value: 'gpt-4.1' },
                    { name: 'gpt-5', value: 'gpt-5' },
                ],
                default: 'llama8b', // значение по умолчанию
            },
            {
                displayName: 'Messages',
                name: 'messages',
                type: 'fixedCollection', // 
                typeOptions: { multipleValues: true },
                default: {},
                options: [
                    {
                        name: 'message',
                        displayName: 'Message',
                        values: [
                            {
                                displayName: 'Role',
                                name: 'role',
                                type: 'options',
                                options: [
                                    { name: 'system', value: 'system' },
                                    { name: 'user', value: 'user' },
                                    { name: 'assistant', value: 'assistant' },
                                ],
                                default: 'user',
                            },
                            {
                                displayName: 'Text',
                                name: 'text',
                                type: 'string',
                                default: '',
                            },
                        ],
                    },
                ],
            },
            {
                displayName: 'Режим вывода',
                name: 'returnMode',
                type: 'options',
                options: [
                    { name: 'Ответ модели', value: 'first' },
                    { name: 'JSON', value: 'raw' },
                ],
                default: 'first',
            },
        ],
    };

    async execute(this: IExecuteFunctions): Promise {
        const items = this.getInputData();
        const output: INodeExecutionData[] = [];

        const creds = await this.getCredentials('amveraLlmApi');
        const token = creds.apiToken as string;

        for (let i = 0; i < items.length; i++) {
            const model = this.getNodeParameter('model', i) as string;
            const messages = this.getNodeParameter('messages.message', i, []) as Array<{ role: string; text: string }>;
            const mode = this.getNodeParameter('returnMode', i) as string;

            const endpoint = model.startsWith('llama') ? 'llama' : 'gpt';
            const body = { model, messages };

            const res = await this.helpers.httpRequest({
                method: 'POST',
                url: `https://kong-proxy.yc.amvera.ru/api/v1/models/${endpoint}`,
                headers: {
                    'X-Auth-Token': `Bearer ${token}`,
                    'Content-Type': 'application/json',
                },
                body,
                json: true,
            });

            let answer = '';

            if (model.startsWith('llama')) {
                const result = res?.result;
                if (result?.alternatives?.length) {
                    answer = result.alternatives[0]?.message?.text ?? '';
                }
            } else {
                if (res?.choices?.length) {
                    answer = res.choices[0]?.message?.content ?? '';
                }
            }
            
            if (mode === 'first') {
                output.push({ json: { text: answer } });
            } else {
                output.push({ json: res });
            }
        }

        return [output];
    }
}

Здесь чуть сложнее, но все также шаблонно.

Первая часть кода

Содержит описание самой ноды для формирования в n8n. Значения большинства параметров описаны в комментариях в коде. Используя шаблон выше вы можете формировать подобные ноды.

Вторая часть кода - execute()

Всё, что происходит внутри метода execute(), - это реальная логика работы ноды.
n8n вызывает этот метод каждый раз, когда workflow доходит до твоей ноды.
В этот момент нода:

  1. получает входные данные;

  2. читает настройки из интерфейса;

  3. делает запрос к API (или любое другое действие), описанное вами в коде;

  4. формирует выходной JSON и передает его дальше.

Разберем метод подробнее.

1. Получаем входные данные

const items = this.getInputData();
const output: INodeExecutionData[] = [];
  • getInputData() возвращает массив объектов (JSON) от предыдущей ноды;

  • output - пустой массив, куда мы будем складывать результаты.

n8n умеет работать с массивами данных - если предыдущая нода передала, например, 10 строк, execute() выполнится 10 раз в цикле.

2. Получаем креды и параметры

const creds = await this.getCredentials('amveraLlmApi');
const token = creds.apiToken as string;
  • getCredentials('amveraLlmApi') - достает токен, который пользователь ввел в Credentials ноды.

Дальше - параметры, которые пользователь задаёт в интерфейсе ноды:

const model = this.getNodeParameter('model', i) as string;
const messages = this.getNodeParameter('messages.message', i, []) as Array<{ role: string; text: string }>;
const mode = this.getNodeParameter('returnMode', i) as string;

Каждый вызов getNodeParameter() получает значение поля из properties (той самой секции в описании ноды).
i - индекс текущего элемента из входных данных (если их несколько).

3. Формируем запрос к API

const endpoint = model.startsWith('llama') ? 'llama' : 'gpt';
const body = { model, messages };

Здесь мы определяем, какой эндпоинт использовать - llama или gpt и формируем body запроса.

4. Отправляем HTTP-запрос

const res = await this.helpers.httpRequest({
    method: 'POST',
    url: `https://kong-proxy.yc.amvera.ru/api/v1/models/${endpoint}`,
    headers: {
        'X-Auth-Token': `Bearer ${token}`,
        'Content-Type': 'application/json',
    },
    body,
    json: true,
});
  • this.helpers.httpRequest - встроенный метод n8n для HTTP-запросов (автоматически обрабатывает JSON, ошибки, HTTPS). Короче говоря: удобно.

  • В заголовок добавляется токен авторизации (Bearer ${token}), который мы получили из кредов.

  • json: true говорит n8n автоматически парсить ответ как JSON.

5. Обрабатываем ответ

Amvera LLM API может вернуть результат в разных форматах - у LLaMA и GPT структура ответа отличается.

let answer = '';

if (model.startsWith('llama')) {
    const result = res?.result;
    if (result?.alternatives?.length) {
        answer = result.alternatives[0]?.message?.text ?? '';
    }
} else {
    if (res?.choices?.length) {
        answer = res.choices[0]?.message?.content ?? '';
    }
}
  • Для моделей LLaMA ответ находится в res.result.alternatives[0].message.text.

  • Для GPT-моделей - в res.choices[0].message.content.

  • ?? '' - оператор, который возвращает строку, если undefined.

6. Формируем выходной результат

if (mode === 'first') {
    output.push({ json: { text: answer } });
} else {
    output.push({ json: res });
}
  • Если выбран режим "Ответ модели", нода вернет только текст (удобно для чат-ботов).

  • Если выбран режим "JSON", в вывод попадёт весь ответ API

7. Возвращаем результат n8n

return [output];

С такими настройками выглядит следующим образом

Настраиваем ноду
Настраиваем ноду
  • Весь код ноды доступен на нашем Github

Публикация пакета

Если у вас уже готов код (т.е. вы настроили вид ноды и ее логику в execute()), пакет нужно опубликовать в npm.

Для этого нужно иметь аккаунт в npm и залогиниться через команду npm login, которая перенаправить вас в браузер для авторизации.

Еще раз убедитесь, что в package.json название пакета начинается с n8n-nodes.

После этого, проект необходимо собрать. Для этого пишем:

npm i
npm run build # если у вас есть скрипт build в package.json для компиляции TS 

Если у вас появились папки node_modules и dist с скомпилированными js файлами, ваш пакет готов к публикации. Пишем:

npm publish --access public

Если все получилось правильно, пакет должен появиться и быть доступен в npm Registry.

Итог

Теперь вы можете подключить опубликованный пакет в n8n и пользоваться нодой в свое удовольствие.

На самом деле, написание нод для n8n, не такая уж сложная задача, как может казаться на первый взгляд. Достаточно лишь по шаблону заполнить описание ноды и описать логику в методе execute().

Таким образом вы можете подключить любой API буквально за вечер, что дает полную свободу.

Комментарии (0)