Серверная валидация является критическим слоем защиты данных в любой системе, где входящие запросы поступают от внешних клиентов. Даже при наличии клиентской проверки нельзя считать данные доверенными: запрос может быть модифицирован, сформирован вручную или отправлен через сторонние инструменты.
Основные задачи серверной валидации:
В контексте Node.js приложений библиотека Superstruct используется как легковесный инструмент для декларативного описания структуры данных и их строгой проверки.
Superstruct строится вокруг идеи композиции структур (structs),
каждая из которых описывает конкретное правило валидации. Вместо
императивных проверок вида if (...) throw используется
декларативное описание схемы.
Ключевые принципы:
В серверной валидации чаще всего используются простые типы: строки, числа, булевы значения, массивы и объекты.
Пример базовых структур:
import { string, number, boolean, validate } from "superstruct";
const Name = string();
const Age = number();
const IsActive = boolean();
const [error, value] = validate("John", Name);
В случае серверной обработки данных это может выглядеть так:
const [error, age] = validate(req.body.age, Age);
if (error) {
res.status(400).json({ error: error.message });
}
Реальные серверные данные редко бывают плоскими. Обычно они содержат вложенные объекты.
Superstruct позволяет описывать сложные структуры через
object:
import { object, string, number } from "superstruct";
const User = object({
name: string(),
age: number(),
email: string(),
});
Применение в API:
const [error, user] = validate(req.body, User);
if (error) {
return res.status(400).json({ error: error.message });
}
Для серверных API часто требуется проверка списков:
import { array, string } from "superstruct";
const Tags = array(string());
Пример запроса:
const [error, tags] = validate(req.body.tags, Tags);
Это особенно полезно для:
Одним из ключевых преимуществ Superstruct является возможность создания пользовательских структур.
import { define } from "superstruct";
const PositiveNumber = define("PositiveNumber", (value) => {
return typeof value === "number" && value > 0;
});
Использование:
const [error, price] = validate(req.body.price, PositiveNumber);
Такой подход позволяет внедрять бизнес-правила прямо в слой валидации.
В типичном Express-подобном сервере схема используется на уровне middleware:
import express from "express";
import { object, string } from "superstruct";
import { validate } from "superstruct";
const app = express();
app.use(express.json());
const CreateUser = object({
name: string(),
email: string(),
});
app.post("/users", (req, res) => {
const [error, data] = validate(req.body, CreateUser);
if (error) {
return res.status(400).json({
message: "Validation error",
details: error.message,
});
}
res.json({ status: "ok", user: data });
});
В более структурированных приложениях валидация выносится в отдельный слой:
const validateBody = (struct) => (req, res, next) => {
const [error, data] = validate(req.body, struct);
if (error) {
return res.status(400).json({ error: error.message });
}
req.body = data;
next();
};
Использование:
app.post("/users", validateBody(CreateUser), handler);
Ошибки в Superstruct возвращаются в стандартизированном формате. Это упрощает построение единых API-ответов.
Рекомендуемый подход:
Пример:
if (error) {
return res.status(422).json({
error: "Invalid payload",
fields: error.failures?.() || [],
});
}
В больших проектах схемы выносятся в отдельные модули:
// structs/user.js
import { object, string } from "superstruct";
export const UserStruct = object({
name: string(),
email: string(),
});
Использование в разных сервисах:
import { UserStruct } from "./structs/user";
validate(req.body, UserStruct);
Superstruct поддерживает преобразование типов через
coerce.
Пример:
import { coerce, number, string } from "superstruct";
const NumberFromString = coerce(number(), string(), (value) =>
Number(value)
);
Это полезно для серверных API, где данные приходят в виде строк:
// req.body.id = "123"
const [error, id] = validate(req.body.id, NumberFromString);
Композиция позволяет строить масштабируемые схемы:
import { object, string, number, array } from "superstruct";
const Post = object({
title: string(),
content: string(),
tags: array(string()),
meta: object({
likes: number(),
comments: number(),
}),
});
Такие структуры часто используются в:
При использовании Superstruct важно учитывать:
Оптимизационные практики:
Серверная валидация не заменяет:
Однако она играет роль первого фильтра входных данных, предотвращая попадание некорректных структур в бизнес-логику.
Типичная архитектура с использованием Superstruct включает:
Валидация выполняется до попадания данных в бизнес-логику, обеспечивая строгий контракт между слоями приложения.