При использовании Tom Select основная логика выбора и ввода значений часто переносится на клиент, однако окончательная проверка данных должна выполняться на сервере. Любые значения, поступающие из компонента, считаются недоверенными: автодополнение, создание новых элементов, мультивыбор и динамическая подгрузка данных формируют поверхность атаки и источник неконсистентности данных.
Серверная валидация в связке с Tom Select решает несколько задач одновременно: контроль допустимых значений, предотвращение подмены идентификаторов, проверка прав доступа к выбранным сущностям и синхронизация клиентского состояния с актуальными данными базы.
Типовой сценарий взаимодействия строится вокруг нескольких точек обмена:
load)search)createItem)submit)Каждый из этих этапов может включать серверную проверку.
Ключевая идея: поиск и отображение — это удобство, валидация — это обязательное правило на сервере.
Метод load часто используется для динамической подгрузки
данных:
new TomSelect("#select", {
load: function(query, callback) {
fetch(`/api/users?q=${encodeURIComponent(query)}`)
.then(res => res.json())
.then(data => callback(data))
.catch(() => callback());
}
});
На сервере в этот момент обычно выполняется:
queryВажно не полагаться на клиентский ввод:
Наиболее критичный этап — финальная отправка данных.
Tom Select формирует массив значений:
{
"tags": ["12", "45", "78"]
}
или в режиме создания новых элементов:
{
"tags": ["react", "vue", "svelte"]
}
app.post("/submit", async (req, res) => {
const { tags } = req.body;
if (!Array.isArray(tags)) {
return res.status(400).json({ error: "Invalid format" });
}
const validTags = await db.tags.findMany({
where: {
id: { in: tags }
}
});
if (validTags.length !== tags.length) {
return res.status(400).json({ error: "Some tags are invalid" });
}
res.json({ ok: true });
});
Здесь ключевой принцип — сравнение количества и состава входных данных с результатами из базы.
Tom Select поддерживает создание новых значений через
create: true:
new TomSelect("#select", {
create: true
});
Это означает, что пользователь может отправить значение, которого нет в системе.
Часто применяется разделение логики:
app.post("/tags", async (req, res) => {
const { tags } = req.body;
const result = [];
for (const tag of tags) {
if (/^[a-zA-Z0-9_-]{2,30}$/.test(tag)) {
const existing = await db.tags.findOne({ name: tag });
if (existing) {
result.push(existing.id);
} else {
const created = await db.tags.create({ name: tag });
result.push(created.id);
}
}
}
res.json({ tags: result });
});
Регулярные выражения и бизнес-валидация на сервере являются обязательными, даже если клиент уже фильтрует ввод.
Одной из типичных проблем является подмена value:
{
"user_id": "1",
"role": "admin"
}
Tom Select может возвращать только UI-слой, но злоумышленник способен отправить произвольный payload.
Иногда требуется проверить значение до отправки формы:
async function validateValue(value) {
const res = await fetch(`/api/validate?id=${value}`);
const data = await res.json();
return data.valid;
}
Применение в Tom Select:
onChange: async function(value) {
const valid = await validateValue(value);
if (!valid) {
this.removeItem(value);
}
}
При быстром вводе:
Ответ может прийти в неправильном порядке.
Пример обработки:
let lastRequestId = 0;
load: function(query, callback) {
const requestId = ++lastRequestId;
fetch(`/api?q=${query}`)
.then(r => r.json())
.then(data => {
if (requestId !== lastRequestId) return;
callback(data);
});
}
Tom Select хранит состояние локально, но сервер является источником истины.
Типичные проблемы:
При каждом открытии списка или перед сохранением:
На сервере обычно вводится несколько уровней проверки:
Любая логика в браузере считается ненадёжной.
Игнорирование промежуточных состояний приводит к накоплению мусора в UI.
Использование label вместо id приводит к подменам.
Сервер должен нормализовать вход:
При мультивыборе важно не валидировать элементы по одному в цикле без оптимизации.
Оптимальный подход:
const ids = req.body.tags;
const valid = await db.tags.findMany({
where: { id: { in: ids } }
});
Затем:
Tom Select выступает как слой UX, а сервер выполняет:
Любая интеграция считается корректной только при условии, что сервер способен полностью восстановить и переоценить состояние выбора без участия клиента.