Function в n8n

Function в n8n: Полное руководство по узлу для выполнения пользовательского кода

Узел Function (Функция) в n8n является одним из наиболее мощных и гибких инструментов платформы. Он позволяет выполнять пользовательский JavaScript код для манипуляции данными, реализации сложной логики, интеграции с внешними библиотеками и обработки информации способами, которые не поддерживаются стандартными узлами. Этот узел предоставляет полный программный контроль над потоком данных, выступая в качестве универсального инструмента для решения нестандартных задач автоматизации.

Принцип работы и архитектура узла Function

Узел Function выполняется в изолированной среде Node.js на стороне сервера, где работает n8n. Он получает входные данные от предыдущих узлов в виде массива объектов. Каждый элемент массива соответствует одному элементу данных (item), который может содержать несколько полей (json, binary и т.д.). Код, написанный пользователем, выполняется для каждого элемента или для всего массива, в зависимости от выбранного режима. Результатом выполнения кода должен быть возврат данных, которые затем передаются следующим узлам в воркфлоу. Узел поддерживает два основных режима работы: «Function» для обработки каждого элемента и «Function Item» для работы со всем массивом элементов.

Структура объекта данных в n8n

Для эффективной работы с узлом Function необходимо понимать структуру данных, с которой он оперирует. Данные в n8n организованы в виде массива объектов, где каждый объект имеет строгую структуру.

Поле объекта Тип данных Описание Пример
json Object Основное поле, содержащее структурированные данные в формате ключ-значение. Это основное поле для манипуляций. { "id": 123, "name": "Product" }
binary Object Содержит бинарные данные (например, файлы). Ключи — имена бинарных свойств, значения — объекты с данными файла. { "image": { "data", "mimeType", "fileName" } }
pairedItem Object | Array Служебное поле, связывающее элемент с исходными данными для корректной работы ошибок и дебаггинга. { "item": 0 }
index Number Индекс элемента в исходном массиве (добавляется автоматически в некоторых случаях). 0

Режимы работы узла Function

1. Режим «Function» (Обработка каждого элемента)

В этом режиме код выполняется один раз для каждого отдельного элемента. Переменные $input, $item и $index автоматически доступны внутри функции.

    • $input или $item: Содержит текущий элемент данных (например, { json: { id: 1 } }).
    • $index: Порядковый номер итерации (начинается с 0).
    • items: Ссылка на полный массив всех элементов (используется реже).

    Для возврата данных необходимо использовать оператор return. Возвращаемый объект будет автоматически обернут n8n в правильную структуру.

    2. Режим «Function Item» (Обработка всего массива элементов)

    В этом режиме код выполняется один раз для всего массива элементов. Переменная $input содержит полный массив всех элементов. Это полезно для агрегации данных, группировки или сложных преобразований, требующих доступа ко всем данным одновременно. В этом режиме вы должны вернуть массив элементов вручную, соблюдая структуру n8n.

    Доступные объекты и функции в контексте выполнения

    Помимо входных данных, среда выполнения узла Function предоставляет ряд встроенных объектов и утилит для расширения возможностей.

    Объект / Функция Назначение Пример использования
    $() (метод jQuery) Упрощенный поиск и фильтрация данных внутри элемента. Позволяет использовать jQuery-подобный синтаксис. $($input.all()).filter({ "json": { "price": { _gt: 100 } } })
    $json, $binary Сокращенные ссылки на поля $item.json и $item.binary. Упрощают доступ к данным. $json.id вместо $item.json.id
    getNodeParameter() Позволяет прочитать значение параметра, установленного в интерфейсе узла Function (например, из выпадающего списка или поля ввода). const threshold = getNodeParameter('threshold');
    getWorkflowStaticData() Возвращает объект для хранения данных между запусками одного и того же воркфлоу (сохранение состояния). const data = getWorkflowStaticData('global'); data.lastId = newId;
    Стандартные объекты Node.js Доступны такие объекты, как Math, Date, JSON, console (для логирования), setTimeout/setInterval (с ограничениями). const today = new Date(); console.log($json);
    Встроенные библиотеки Предустановленные npm-пакеты: lodash (как _), moment (для дат), axios для HTTP-запросов, cheerio для парсинга HTML. const sum = _.sumBy(items, 'json.price');

    Практические примеры использования узла Function

    Пример 1: Преобразование и обогащение данных

    Задача: преобразовать дату из строкового формата, рассчитать итоговую стоимость с учетом налога и добавить поле-статус.

    
    // Режим: Function (выполняется для каждого элемента)
    const rawDate = $json.orderDate; // "2023-12-31"
    const amount = $json.amount;
    
    // Используем moment для работы с датами
    const formattedDate = moment(rawDate).format('DD.MM.YYYY');
    // Расчет налога (20%)
    const tax = amount 
  • 0.2;
  • const total = amount + tax; // Логика для статуса const status = total > 1000 ? 'PREMIUM' : 'STANDARD'; // Возвращаем новый объект. Старые поля можно сохранить или перезаписать. return { json: { ...$json, // Распаковываем все старые поля orderDate: formattedDate, tax: Math.round(tax
  • 100) / 100, // Округление
  • total: Math.round(total
  • 100) / 100,
  • status: status } };

    Пример 2: Агрегация данных в режиме Function Item

    Задача: получить массив всех заказов и сгруппировать их по клиентам, подсчитав общую сумму и количество заказов для каждого.

    
    // Режим: Function Item (обрабатываем весь массив items)
    const orders = $input.all();
    
    // Используем lodash для группировки
    const grouped = _.groupBy(orders, item => item.json.customerId);
    
    const result = [];
    
    for (const [customerId, customerOrders] of Object.entries(grouped)) {
      const totalAmount = _.sumBy(customerOrders, 'json.amount');
      const averageAmount = totalAmount / customerOrders.length;
    
      result.push({
        json: {
          customerId: customerId,
          numberOfOrders: customerOrders.length,
          totalAmount: totalAmount,
          averageOrderValue: Math.round(averageAmount 
  • 100) / 100,
  • // Можно добавить список ID заказов orderIds: customerOrders.map(order => order.json.id) } }); } // Возвращаем новый массив элементов return result;

    Пример 3: Работа с бинарными данными и внешними HTTP-запросами

    Задача: для каждого элемента, содержащего бинарный файл изображения, отправить его на внешний API для обработки и получить метаданные.

    
    // Режим: Function
    // Предполагаем, что у элемента есть binary поле с ключом 'image'
    if ($binary.image) {
      // Для работы с бинарными данными часто нужен Buffer
      // Данные бинарного файла доступны как Buffer через .data
      const imageBuffer = $binary.image.data;
    
      // Пример: отправка файла на внешний сервис с помощью axios
      try {
        const formData = new FormData();
        // axios в n8n адаптирован для работы с Buffer
        formData.append('file', imageBuffer, { filename: $binary.image.fileName });
    
        const response = await axios.post('https://api.example.com/analyze', formData, {
          headers: formData.getHeaders()
        });
    
        // Возвращаем исходные данные + результат анализа
        return {
          json: {
            ...$json,
            imageAnalysis: response.data
          },
          binary: $item.binary // Сохраняем исходные бинарные данные
        };
      } catch (error) {
        // В случае ошибки можно выбросить исключение или вернуть элемент с флагом ошибки
        throw new Error(`Ошибка анализа изображения: ${error.message}`);
      }
    }
    // Если изображения нет, возвращаем элемент как есть
    return $item;
    

    Обработка ошибок и отладка

    Корректная обработка ошибок в узле Function критически важна для надежности воркфлоу.

    • Использование try...catch: Все операции, которые могут вызвать ошибку (HTTP-запросы, парсинг данных), должны быть обернуты в блоки try-catch.
    • Выброс ошибок: Используйте throw new Error('Описание ошибки') для остановки выполнения узла и передачи ошибки по потоку. Это активирует порт "Error" (если он подключен).
    • Логирование: Используйте console.log(), console.warn(), console.error() для вывода отладочной информации. Логи видны во вкладке "Execution" при просмотре конкретного выполнения воркфлоу.
    • Валидация данных: Всегда проверяйте наличие ожидаемых полей перед доступом к ним.

    Производительность и лучшие практики

    • Избегайте синхронных тяжелых операций: Код выполняется в основном потоке Node.js. Длительные синхронные вычисления заблокируют цикл событий. Используйте асинхронные операции или разбивайте задачи.
    • Кэширование данных: Для данных, не меняющихся между запусками, используйте getWorkflowStaticData().
    • Эффективное использование памяти: При обработке тысяч элементов в режиме Function Item избегайте создания чрезмерно больших промежуточных массивов.
    • Модульность: Для сложного кода рассмотрите возможность вынесения логики в отдельные скриптовые узлы или внешние ресурсы.
    • Комментарии и форматирование: Код должен быть хорошо документирован, так как он является частью бизнес-логики автоматизации.

    Интеграция с внешними библиотеками

    Хотя ряд библиотек доступен по умолчанию, иногда требуется подключить специфический npm-пакет. Начиная с версии n8n 0.198.0+, существует возможность устанавливать дополнительные npm-пакеты напрямую в интерфейсе узла Function. Для этого в редакторе кода есть вкладка "Libraries". Альтернативно, пакеты можно установить глобально в среде выполнения n8n (на сервере), что требует доступа к командной строке и перезагрузки n8n.

    Ответы на часто задаваемые вопросы (FAQ)

    Вопрос 1: В чем разница между узлами Function, Function Item и Code?

    Ответ: Узел "Function" выполняет код для каждого элемента отдельно. Узел "Function Item" выполняет код один раз для всего массива элементов, предоставляя больше контроля над выходным массивом. Узел "Code" (появился позже) предлагает альтернативный, более структурированный интерфейс с предопределенными переменными и шаблонами, но суть та же — выполнение пользовательского JavaScript/TypeScript кода.

    Вопрос 2: Как сохранить данные между запусками одного и того же воркфлоу?

    Ответ: Используйте функцию getWorkflowStaticData(type). Параметр type может быть 'global' (данные доступны всем узлам воркфлоу) или 'node' (данные доступны только этому конкретному узлу). Данные сохраняются в базе данных n8n.

    
    const staticData = getWorkflowStaticData('node');
    // Инициализация счетчика
    if (staticData.counter === undefined) {
      staticData.counter = 0;
    }
    staticData.counter++;
    // staticData.counter сохранится для следующего запуска
    return { json: { runCount: staticData.counter } };
    

    Вопрос 3: Почему мой код возвращает "undefined" или следующий узел не получает данные?

    Ответ: Наиболее частая причина — отсутствие оператора return в режиме "Function" или возврат не того формата в режиме "Function Item". Убедитесь, что:

    • В режиме "Function" вы возвращаете один объект (например, { json: { ... } }).
    • В режиме "Function Item" вы возвращаете массив объектов той же структуры.
    • В коде нет необработанных ошибок, прерывающих выполнение.

    Вопрос 4: Как сделать HTTP-запрос внутри узла Function?

    Ответ: Используйте предустановленную библиотеку axios. Всегда обрабатывайте ошибки с помощью try...catch.

    
    try {
      const response = await axios.get('https://api.example.com/data', {
        params: { id: $json.id }
      });
      return { json: { ...$json, apiData: response.data } };
    } catch (error) {
      // Можно выбросить ошибку или вернуть элемент с индикацией проблемы
      return { json: { ...$json, apiError: error.message } };
    }
    

    Вопрос 5: Можно ли импортировать собственные JavaScript-модули?

    Ответ: Прямой импорт через import или require() из локальных файлов в облачной среде n8n обычно невозможен. Альтернативы:

    • Установить нужную функциональность как npm-пакет через вкладку "Libraries".
    • Скопировать исходный код функции прямо в редактор узла.
    • В self-hosted окружении можно модифицировать файловую систему сервера, но это не рекомендуется и усложняет переносимость воркфлоу.

Вопрос 6: Как обрабатывать бинарные данные (файлы) в Function узле?

Ответ: Бинарные данные доступны через $item.binary. Каждое бинарное свойство содержит объект с полями data (Buffer), mimeType и fileName. Вы можете читать, модифицировать (если это Buffer) или создавать новые бинарные свойства.


// Чтение
const fileBuffer = $binary.attachment.data;
// Создание нового бинарного поля (например, из строки)
const newBuffer = Buffer.from('Hello, world!', 'utf8');
return {
  json: $json,
  binary: {
    ...$binary, // Сохраняем старые бинарные данные
    textFile: {
      data: newBuffer,
      mimeType: 'text/plain',
      fileName: 'greeting.txt'
    }
  }
};

Вопрос 7: Есть ли ограничения на время выполнения или использование памяти?

Ответ: Да, ограничения зависят от способа развертывания n8n. В облачной версии (n8n.cloud) действуют строгие лимиты на время выполнения одного узла (порядка 1-2 минут) и потребление памяти. В self-hosted версии ограничения определяются ресурсами сервера и конфигурацией Node.js. Длительные операции следует разбивать на этапы или выносить во внешние сервисы.

Заключение

Узел Function в n8n — это ключевой инструмент для реализации сложной, нестандартной логики в автоматизации. Он стирает границы возможного, позволяя разработчику использовать всю мощь JavaScript и экосистемы Node.js для обработки данных. Понимание его режимов работы, структуры данных, доступных утилит и лучших практик позволяет создавать надежные, эффективные и мощные воркфлоу. Несмотря на появление более специализированных узлов, Function остается незаменимым решением для задач, требующих максимальной гибкости и программного контроля.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *