Первый workflow: ручной запуск → редактирование полей → код
Я два года считал цену вручную в таблице: количество умножить на цену за единицу, прибавить доставку, потом разделить обратно, чтобы получить стоимость за штуку. Двести пакетов, цена за единицу один юань восемьдесят пять фэней, доставка шестьдесят, в сумме четыреста тридцать юаней, за штуку два юаня пятнадцать фэней. Числа несложные, проблема в том, что я дважды пропустил доставку, а коммерческое предложение клиенту уже было отправлено.
Позже этот алгоритм переехал в n8n, три узла, меняешь одно число и запускаешь снова, за две секунды новое значение. Эта статья — те три шага от пустого холста до рабочего запуска, включая ту ошибку, когда я в первый раз посчитал 430 как 37060.
Предварительно нужно только две вещи: n8n уже установлен и открывается (если не установлен, смотрите«Как установить n8n»), холст, панель узлов и журнал выполнений знакомы (если нет, откройте«Обзор интерфейса»). Уметь писать код не нужно, блок с кодом можно просто скопировать целиком.
- Каждый из трёх узлов отвечает за одну задачу
- Что именно передаётся между узлами
- От пустого холста до трёх узлов
- Узел редактирования полей: добавить к одной записи четыре поля
- Узел кода: взять одну, посчитать один раз, вернуть одну
- Запустить один раз, посмотреть вывод
- Приложение: полный JSON рабочего процесса
- Как убедиться, что действительно работает
- Пять ошибок, которые действительно возникают
- Три границы узла Code
Три узла, каждый отвечает за одно
Самая частая ошибка новичков — воспринимать три узла как «три шага». Это не шаги, это три роли.
- Manual Trigger (ручной запуск): создаёт одну пустую запись данных и передаёт её дальше. Он не обращается к базе данных, не принимает запросы, не следит за временем — это чисто «вы нажали один раз, он один раз выполнился». В первом рабочем процессе он нужен, чтобы сократить переменные до одной — вопрос «откуда берутся данные» пока можно не рассматривать.
- Edit Fields (редактирование полей): добавляет поля в item или изменяет существующие. Количество записей не меняется: сколько вошло, столько и вышло. В старых руководствах он назывался Set, переименован он уже давно, в панели узлов версии 2.43.7 написано Edit Fields (Set), по обоим именам можно найти.
- Code (код): единственный шаг, который может изменить количество записей. Одна запись может быть разбита на несколько, несколько могут быть сжаты в одну. Поэтому его возвращаемое значение должно быть массивом — это его главное отличие от других узлов и место, где возникает больше всего ошибок.
Мой экземпляр — версии 2.43.7, интерфейс переключён на упрощённый китайский. Нужно предупредить: китайский интерфейс переводит только описательный текст и кнопки, названия узлов и параметров остаются на английском. Если вы ищете «编辑字段», вы ничего не найдёте, нужно искать edit fields. Те руководства в интернете, где написано «узел Set» или «Keep Only Set Fields узла Set», точно так же не соответствуют интерфейсу 2.x — не сомневайтесь в себе.
Что на самом деле передаётся между узлами
Если не разобраться в этом, каждую последующую ошибку можно будет только угадывать.
Все данные, циркулирующие между узлами в n8n, — это массив, и каждый элемент в массиве выглядит так:
[
{ "json": { "qty": 200 } },
{ "json": { "qty": 150 } }
]
Внешние квадратные скобки — это «пакет», внутри каждый объект является единицей данных, n8n называет это item. Настоящие бизнес-поля вложены в json этот ключ. Поэтому «обработать 200 заказов» в этой модели означает не цикл из 200 итераций, а однократную передачу узлу 200 item, и узел сам обрабатывает их по одному.
Такая архитектура имеет два следствия. Хорошая новость: узлы естественным образом работают пакетно, писать циклы не нужно. Плохая новость: когда пишешь код, на руках у тебя массив, а не отдельный объект —row.qty такую запись нельзя напрямую применить к массиву, сначала нужно извлечь первый элемент.
От пустого холста до трёх узлов
- В правом верхнем углу списка рабочих процессов — «新建工作流», или в адресной строке сразу ввести
/workflow/new. Я привык ко второму способу, быстрее. - На пустом холсте нажми в правом верхнем углу ту +(можно также нажать плюс в центре холста), появится панель «Что произойдёт дальше?».
- В поле поиска введи
manual trigger, выбери Manual Trigger. - Наведи курсор на маленькую точку справа от узла, появится +, нажми на неё, найди
edit fields, выбери Edit Fields (Set). Добавляйте этим способом — связи соединятся сами, вручную перетаскивать не нужно. - Повторите ещё раз, найдите
code, выберите Code. - Имя узла по умолчанию на английском. Двойной щелчок по заголовку позволяет изменить его; я поменял на китайский: ручной запуск, заполнение формы расчёта, вычисление суммы. Когда узлов много, английские имена вроде Trigger, Set, Code при беглом просмотре рябят в глазах; переименование на китайский — чисто для экономии зрения, на функциональность никак не влияет.

Редактирование полей: добавить четыре поля к одной записи данных
Двойной щелчок по «Заполнение формы расчёта» открывает панель; из параметров нужно изменить только два места.
Mode Оставьте Manual Mapping— это способ заполнения через форму. Другой вариант JSON Output — писать JSON напрямую, удобнее для вложенных структур; на начальном этапе пока не используйте.
Fields to Set добавьте четыре строки. В каждой строке слева — имя поля, в середине — выпадающий список типа, справа — значение:
| Имя поля | Тип | 值 |
|---|---|---|
| product | строка | вакуумный компрессионный пакет 60x80cm |
| qty | число | 200 |
| unit_price | число | 1.85 |
| freight | число | 60 |
Выпадающий список типа по умолчанию — «строка», но qty, unit_price и freight нужно вручную переключить на «число». Интерфейс вас не остановит: если ошибётесь, он не выдаст ошибку, а молча сохранит 200 как "200". Насколько это больно — покажу на расчёте чуть позже.

row.unit_price, не хватит одного символа — получите undefined.Самый нижний на панели Include Other Input Fields переключатель по умолчанию включён, что означает «поля на входе, которые я не трогал, выводятся как есть». На входе этого сценария пусто, так что включён он или выключен — без разницы. Но в сценарии с данными выключить его — значит отрезать все поля от предыдущего шага, оставив только четыре, заполненные вручную, — и если потом в коде не получится взять product, не думайте, что код написан с ошибкой.
Узел кода: взять одну, посчитать раз, вернуть одну
Дважды щёлкните «计算金额».Mode Оставить Run Once for All Items(весь массив выполняется один раз),Language Оставить JavaScript. Затем вставьте следующий фрагмент в область редактирования:
const row = items[0].json;
const amount = row.qty * row.unit_price + row.freight;
const unitCost = Number((amount / row.qty).toFixed(2));
return [{
json: {
product: row.product,
qty: row.qty,
amount,
unitCost,
summary: row.product + ' ' + row.qty + ' 个,含运费合计 ' + amount + ' 元,单个成本 ' + unitCost + ' 元',
},
}];
Разберу построчно, только те места, которые могут укусить.
items[0].json Берётся первый элемент. Здесь только одна запись данных, поэтому достаточно взять [0]. Для случая с несколькими записями нужно писать items.map(...), это тема следующей статьи.items — старый способ записи, в 2.x ещё работает; новый способ — $input.first().json, оба варианта на этом экземпляре дали совершенно одинаковый результат. Я оставил items, потому что в режиме «Run Once for All Items» у вас на руках и так весь массив, и это имя честнее.
toFixed(2) Это не для красоты. 430 делить на 200 ровно 2.15, выглядит аккуратно; если заменить на стоимость доставки 55, получится 2.175, при вычислении с плавающей точкой может выйти 2.1749999999999998. Записать это в таблицу или отправить клиенту — такой хвост выглядит некрасиво. Внешний слой Number() Это преобразование его из строки обратно в число —toFixed возвращается строка, и без преобразования unitCost в таблице будет выровнен по левому краю.
Последний фрагмент return — это ключевой момент.Возвращается массив, каждый элемент обёрнут в слой json. n8n по этому ключу json различает «это бизнес-данные» и «это бинарное вложение». Если формат неверный, узел сразу подсветится красным.

Запусти один раз, посмотри вывод
Нажми оранжевую кнопку «执行» под холстом. Через одну-две секунды рамки трёх узлов станут зелёными, в правом верхнем углу каждого узла появится галочка, а на соединительной линии будет отмечено «1 个项目».

Дважды кликни «计算金额», в правой области вывода переключись на вкладку JSON:

Кстати, о том, что легко понять неправильно: кнопка «执行» запускает только по нажатию. Рабочий процесс Manual Trigger не запускается сам по себе после публикации — у него нет механизма «самозапуска». Для запуска по расписанию нужно заменить его на триггер типа Schedule Trigger.
Приложение: JSON всего рабочего процесса
Если лень кликать по узлам, можно импортировать целиком. Скопируй следующий фрагмент и нажми на пустом месте холста Ctrl+V, три узла сразу попадут на холст. Можно и через меню: в правом верхнем углу ⋯ → Импорт → Импорт из файла, сначала сохраните содержимое в .json。
Этот фрагмент я сам вставил в пустой workflow и прогнал: три узла, две связи, позиции совпадают, после выполнения amount тоже 430.
{
"name": "第一个工作流",
"nodes": [
{"parameters":{},"id":"m1","name":"手动触发","type":"n8n-nodes-base.manualTrigger","typeVersion":1,"position":[-368,0]},
{"parameters":{"assignments":{"assignments":[
{"id":"a1","name":"product","value":"真空压缩袋 60x80cm","type":"string"},
{"id":"a2","name":"qty","value":200,"type":"number"},
{"id":"a3","name":"unit_price","value":1.85,"type":"number"},
{"id":"a4","name":"freight","value":60,"type":"number"}
]},"includeOtherFields":true,"options":{}},"id":"m2","name":"填写报价单","type":"n8n-nodes-base.set","typeVersion":3.5,"position":[-144,0]},
{"parameters":{"jsCode":"const row = items[0].json;nconst amount = row.qty * row.unit_price + row.freight;nconst unitCost = Number((amount / row.qty).toFixed(2));nnreturn [{n json: {n product: row.product,n qty: row.qty,n amount,n unitCost,n summary: row.product + ' ' + row.qty + ' 个,含运费合计 ' + amount + ' 元,单个成本 ' + unitCost + ' 元',n },n}];","mode":"runOnceForAllItems"},"id":"m3","name":"计算金额","type":"n8n-nodes-base.code","typeVersion":2,"position":[80,0]}
],
"connections": {
"手动触发": {"main": [[{"node": "填写报价单", "type": "main", "index": 0}]]},
"填写报价单": {"main": [[{"node": "计算金额", "type": "main", "index": 0}]]}
},
"settings": {"executionOrder": "v1"}
}
Как понять, что действительно всё работает
Четыре пункта, все проверяются самостоятельно, без любого из них нельзя считать, что готово.
- Три узла в зелёных рамках с галочкой, на обеих связях отмечено «1 элемент». Если число не 1, значит, промежуточный узел изменил количество, нужно вернуться и проверить.
- В JSON на выходе узла кода amount равен 430, unitCost равен 2.15.
- В истории выполнений можно найти этот запуск. Ручной запуск тоже оставляет запись, не думайте, что в список попадают только задачи по расписанию.
- Измените freight с 60 на 100 и запустите снова, amount должен стать 470. Изменить один вход и увидеть, как меняется выход — этот пункт жёстче предыдущих трёх: увидеть 430 один раз может быть совпадением.
Четвёртый пункт я рекомендую делать каждый раз. Между «запустилось один раз» и «работает правильно» есть разница.
Пять ошибок, которые реально возникнут
1. Code doesn't return items properly
Вернулась строка. Я на практике написал всего одну строку return "共 430 元";, узел сразу становится красным, в правом нижнем углу появляется это уведомление.

Интересно то, чтоreturn { amount: 430 } такая запись без обёртки в массив и без jsonработает. Узел Code автоматически добавит ключ json и квадратные скобки, это поведение описано в документации. Так что эта ошибка не про «недостаточно стандартный формат», а про «то, что ты возвращаешь, вообще не данные» — строку, число, null обернуть нельзя.
2. items is not defined [line 1]
Mode выбран Run Once for Each Item, а в коде по-прежнему используется items. В режиме по одному элементу n8n подаёт за раз только один элемент, текущий называется $json, для получения количества нужно писать $json.qty. Разница между двумя режимами в одной фразе: весь массив за один прогон или каждый элемент за отдельный прогон. Перед написанием кода сначала посмотри на этот выпадающий список.
3. В одном workflow допускается только один узел "Manual Trigger"
Добавляю ещё один триггер в workflow, где триггер уже есть. В этот раз я ошибся ещё глупее: вставил JSON с триггером на холст существующего workflow, всплыло «无法插入节点», а под ним эта строка. Чтобы попробовать для сравнения, сначала создай новый workflow и потом вставляй.
4. Выводится undefined, ошибки нет
В Edit Fields поле называется unit_price, а в коде написано row.unitPrice. Разница в один символ — регистр и подчёркивание, n8n это не проверяет, JS тоже, в результате undefined передаётся дальше, и в summary появляется «含运费合计 undefined 元». Такую ошибку искать сложнее всего, потому что она не подсвечивается красным, не издаёт звука — просто число неверное.
5. amount превращается в "37060"
Тип поля выбран string. Шестьдесят и "60" в JS выглядят одинаково, но + при встрече со строкой переключается на конкатенацию: 370 плюс "60" даёт "37060", а не 430.

Пятый пункт — самый ценный в этой статье. Когда я впервые получил это число, я полминуты смотрел на 37060, прежде чем понял, что это конкатенация строк — потому что код выглядит абсолютно нормально, а проблема в выпадающем списке типа в предыдущем узле.
Три границы узла Code
Узел Code выполняется в собственном процессе n8n, но в sandbox: не может читать локальные файлы, не может отправлять HTTP-запросы. Для работы с файлами используйте узел Read/Write File From Disk, для запросов — узел HTTP Request. Это не урезанная функциональность, а сведение к минимуму того, «что может натворить кусок JS, вставленный наугад».
Но три вещи, которые он не блокирует, придётся контролировать самому.
Не вставляйте наугад JSON workflow, полученный от других.Вставить его — значит выполнить чужой JS на своём экземпляре. На моём экземпляре вставка JSON сразу создаёт узлы, без подтверждения и без вопроса об источнике. Если действительно нужно попробовать, сначала закиньте в пустой тестовый экземпляр, просмотрите код и только потом решайте, запускать ли.
Не хардкодьте секреты в коде.Рабочий процесс можно экспортировать целиком, отправить коллеге, вставить в чат. Жёстко прописанный в коде api key уедет вместе с JSON. Нужно вынести его в узел учётных данных, чтобы n8n хранил его в зашифрованном виде.
Self-hosting позволяет устанавливать сторонние npm-пакеты.По умолчанию выключено, нужно вручную включить переменную окружения. Включение открывает лазейку для произвольного выполнения кода в узле Code. На экземпляре для одного пользователя — как угодно, на общем экземпляре я рекомендую держать это выключенным всегда.
Краткое резюме
Три узла, одна цепочка: триггер создаёт данные, Edit Fields дополняет данные, Code вычисляет данные. Запоминать нужно не то, как кликать по этим трём узлам, а ту форму «массив из json». Дальше IF, Merge, циклы, ветки ошибок — всё это вариации данной формы.
Код — только когда без него никак. Эту смету можно посчитать и через Edit Fields с выражениями,{{ $json.qty * $json.unit_price + $json.freight }} одна строка — и на один узел меньше. Я в итоге выбрал Code, потому что дальше нужно одновременно считать смету по трём вариантам, и нагромождение выражений в узле сложнее поддерживать, чем код. Если считаешь только одно число — не лезь в код.
Насчёт той ловушки с типами — советую прямо сейчас намеренно поменять freight на строку и прогнать один раз, своими глазами увидеть, как 430 превращается в 37060. Десять раз прочитать текст — не то же самое, что один раз ошибиться самому.
Идём дальше
Предыдущая статья —«n8n, Zapier, Make: как выбрать». Как выглядят узлы, связи и выполнение в JSON,эта статьяразбирала. Если однажды сервис не запустится, порт займётся, смотри«Как диагностировать ошибки запуска n8n и занятость портов». Установка смотриэту статью, если интерфейс незнаком, откройэту статью. Оглавление всего курса находитсяздесь。
Справочные материалы
- Manual Trigger: запускается только при нажатии Execute, автоматический запуск не предусмотрен; в одном workflow допускается только один
- Edit Fields (Set): два режима — Manual Mapping и JSON Output, роль Include in Output
- Code: различия двух Mode, параметры языка, отсутствие доступа к файловой системе и возможность отправки HTTP-запросов
- Understand n8n's data structure: происхождение вложенных массивов в json и пояснение автоматического добавления ключа json в узле Code
- Используйте встроенные ярлыки: встроенные переменные и методы в выражениях, какие из них не применимы в узле Code
Последнее обновление: 28 октября 2026 г., использовалась версия n8n 2.43.7.

