Урок курса

Чистые данные: нормализация и валидация

n8n с нуля: автоматизация заявок с AI за вечер

Форма настроена, тестовая отправка работает. Но данные, которые приходят из формы, — сырые: пользователь мог написать имя с пробелами, email заглавными буквами или оставить поле пустым. Перед тем как передавать эти данные дальше по цепочке, их нужно привести к единому виду — иначе любая последующая проверка или поиск по значению будет ненадёжной.

Нормализация строковых полей через Edit Fields

Нормализация — это приведение значений к единообразному виду до того, как они попадут в любую следующую логику. Без неё два item с "Алексей" и " алексей " будут выглядеть как разные значения, хотя содержат одно и то же имя. В n8n нормализация строк делается прямо внутри узла Edit Fields через expression. Никаких дополнительных узлов не нужно — достаточно вписать JavaScript-выражение в поле значения.

Три метода, которые покрывают большинство случаев:

  • .trim() — убирает пробелы в начале и конце строки. Пользователи часто случайно добавляют их при вводе.
  • .toLowerCase() / .toUpperCase() — унифицирует регистр. Особенно важно для email: User@Mail.com и user@mail.com — одно и то же, но без нормализации они не совпадут.
  • .replace() — заменяет нежелательные символы. Например, .replace(/\s+/g, ' ') сжимает несколько пробелов подряд в один.

Методы можно объединять в цепочку: каждый следующий применяется к результату предыдущего. В итоге " User@Mail.COM " превращается в "user@mail.com" одним выражением:

{{ $json['Email'].trim().toLowerCase() }}

Однако у этой записи есть ограничение: она предполагает, что ключ Email в JSON item существует. Если поле не было заполнено и ключ отсутствует, $json['Email'] вернёт undefined — и .trim() вызовет ошибку выполнения. Поэтому в реальном workflow используют тернарный оператор:

{{ $json['Email'] ? $json['Email'].trim().toLowerCase() : '' }}

Логика: если значение существует — применяем преобразование, иначе подставляем пустую строку. Для поля имени аналогично:

{{ $json['Имя'] ? $json['Имя'].trim() : '' }}

Подробнее о том, почему отсутствующий ключ вызывает ошибку и как с этим работать, разберём в разделе про Stop and Error.

В Edit Fields для каждого поля нужно переключиться в режим Expression и прописать преобразование. После сохранения узла проверьте панель Output — там должны быть уже приведённые значения: без лишних пробелов, email строчными буквами. Если expression написан с ошибкой, узел окрасится в красный прямо здесь, до IF.

Нормализация — это первый узел после Form Trigger. Она не проверяет, заполнены ли поля — только приводит значения к нужному виду. Проверка наличия обязательных данных — следующий шаг, который делает узел IF.

Валидация обязательных полей узлом IF

После того как Edit Fields привёл значения к нужному виду, нужно ответить на вопрос: а есть ли там вообще что-то полезное? Именно для этого к цепочке подключается узел IF.

IF работает просто: он проверяет заданное условие для каждого item и отправляет его в одну из двух веток — true или false. В контексте валидации удобнее мыслить от обратного: условие описывает проблему, а не норму.

Если условие формулируется как «поле пустое», то:

  • ветка true — данные невалидны, item нужно остановить;
  • ветка false — данные в порядке, обработка продолжается дальше.

Простейшее условие для проверки email выглядит так:

{{ $json.email === '' }}

Но здесь есть ловушка. Если пользователь вообще не заполнил поле, которое в форме помечено как необязательное, n8n может не создать этот ключ в JSON item вовсе. Тогда $json.email равен не пустой строке, а undefined — и условие === '' вернёт false. Невалидный item проскользнёт в ветку обработки, как будто всё нормально.

Надёжное условие закрывает оба случая сразу:

{{ !$json.email || $json.email.trim() === '' }}

!$json.email срабатывает, если ключ отсутствует или равен undefined/null/пустой строке. .trim() === '' добавляет защиту от строки, которая выглядит непустой, но состоит только из пробелов — например, если пользователь случайно нажал пробел и отправил форму.

В интерфейсе n8n условие IF настраивается в разделе Conditions: выбираем тип значения — Expression, и вставляем выражение выше. Можно добавить несколько условий и объединить их через AND/OR — например, проверять сразу имя и email.

Когда условие срабатывает (ветка true), item уходит дальше по этой ветке — туда будет подключён узел остановки. Ветка false при этом продолжает цепочку с чистыми и проверенными данными.

Остановка невалидного item: узел Stop and Error

Когда IF выявил проблему и отправил item в ветку true, нужно явно завершить выполнение — не просто «ничего не делать», а остановить цепочку с понятным сообщением. Для этого в n8n есть узел Stop and Error.

Его логика проста: как только item достигает этого узла, workflow прекращает выполнение и фиксирует ошибку с заданным текстом. Никакие последующие узлы не получат этот item.

В настройках Stop and Error одно ключевое поле — Error Message. Пишите туда что-то конкретное, а не «Ошибка»:

  • Email обязателен — заявка не принята
  • Не заполнено поле Имя
  • Описание не может быть пустым

Конкретное сообщение помогает при отладке: если workflow упадёт в продакшне, вы сразу поймёте, какое именно поле стало причиной.

Когда Stop and Error срабатывает во время тестирования, узел окрашивается в красный — это нормальное и ожидаемое поведение, не признак поломки workflow. В панели Output видно текст сообщения об ошибке. Именно так и должна выглядеть успешно работающая валидация: невалидный item остановлен, цепочка дальше не пошла.

Ловушка, которая проявляется раньше Stop and Error

Есть ситуация, при которой узел Edit Fields краснеет ещё до IF и до Stop and Error. Это происходит, если поле формы вообще отсутствует в JSON item — например, пользователь не заполнил необязательное поле, и n8n не создал для него ключ. Тогда expression вида:

{{ $json['Имя'].trim() }}

выбрасывает ошибку Cannot read properties of undefined (reading 'trim'). Потому что $json['Имя'] возвращает undefined, а у undefined нет метода .trim().

Решений два. Первое — пометить поле как Required прямо в настройках Form Trigger: тогда n8n не даст отправить форму без его заполнения. Второе — защитить expression тернарным оператором:

{{ $json['Имя'] ? $json['Имя'].trim() : '' }}

Здесь: если $json['Имя'] существует и непустое — применяем .trim(), иначе подставляем пустую строку. IF потом поймает эту пустую строку своим условием и отправит item в Stop and Error. Цепочка работает корректно.

Выбор между двумя подходами зависит от контекста: если поле действительно обязательное, лучше помечать его Required в форме — это останавливает проблему ещё до попадания в workflow. Тернарный оператор нужен для полей, которые могут прийти пустыми по задумке, но всё равно участвуют в нормализации.

Сборка полного workflow: нормализация → валидация → остановка

Теперь собираем всё в одну цепочку. Никаких новых узлов — только то, что уже разобрано, в правильном порядке.

Шаг 1. Form Trigger

Откройте workflow из предыдущего урока. Form Trigger уже настроен: поля «Имя», «Email», «Описание». Если Email помечен как Required, снимите пометку на время теста — нужно проверить, что валидация в IF работает даже когда форма пропускает пустое поле.

Шаг 2. Edit Fields — нормализация

Добавьте узел Edit Fields сразу после Form Trigger и подключите его. Создайте три поля, используя тернарный оператор из предыдущего раздела — он защищает от отсутствующего ключа:

  • name → Expression: {{ $json['Имя'] ? $json['Имя'].trim() : '' }}
  • email → Expression: {{ $json['Email'] ? $json['Email'].trim().toLowerCase() : '' }}
  • description → Expression: {{ $json['Описание'] ? $json['Описание'].trim() : '' }}

После сохранения узла запустите тест формы с корректными данными и проверьте Output Edit Fields — там должны быть уже приведённые значения: без лишних пробелов, email строчными буквами.

Шаг 3. IF — проверка валидности

Добавьте узел IF и подключите его к Edit Fields. В разделе Conditions добавьте условие с типом Expression:

{{ !$json.email || $json.email.trim() === '' }}

Ветка true — невалидный item, ветка false — данные в порядке. Если нужно проверять несколько полей сразу, добавьте второе условие и объедините через OR:

{{ !$json.name || $json.name.trim() === '' }}

Шаг 4. Stop and Error — ветка true

Добавьте узел Stop and Error и подключите его к выходу true узла IF. В поле Error Message напишите конкретное сообщение:

Email обязателен — заявка не принята

Ветку false оставьте пока незаполненной — туда в следующем уроке подключится маршрутизация.

Шаг 5. Тестовый запуск с невалидными данными

Откройте форму по тестовой ссылке Form Trigger и отправьте заявку с пустым Email:

{ "Имя": "  алексей ", "Email": "", "Описание": "Не работает вход" }

Что должно произойти:

  1. Edit Fields обработает item, поле email станет пустой строкой ''.

  2. IF получит item: условие !$json.email || $json.email.trim() === '' вернёт true.

  3. Item уйдёт в ветку true → достигнет Stop and Error.

  4. Узел Stop and Error окрасится в красный, в панели Output появится текст «Email обязателен — заявка не принята».

Это ожидаемый результат. Красный узел — не поломка, а подтверждение: валидация работает, некорректный item остановлен, дальше по цепочке ничего не пошло.

Для полноты картины отправьте форму ещё раз с заполненным Email. IF должен отправить item в ветку false — она пока ни к чему не подключена, и n8n просто не будет делать ничего дальше. Главное — Stop and Error при этом не срабатывает.

Цепочка готова. В уроке 6 к ветке false IF подключится логика маршрутизации — нормализованные и проверенные данные будут использоваться для разделения заявок на обычные и приоритетные.

Поток нормализации, валидации и остановки невалидной заявки

Поток нормализации, валидации и остановки невалидной заявки.

Попробуйте решить

Ты хочешь привести поле «Имя», пришедшее из формы, к единообразному виду: убрать пробелы по краям и сделать все буквы строчными. Какая expression в узле Edit Fields решит эту задачу?

Продолжить с проверкой и прогрессом

Откройте интерактивный раннер с заданиями урока.

Перейти к интерактивному уроку