В серверных приложениях на базе Koa проверка входных данных реализуется на уровне middleware, что позволяет отделить бизнес-логику от контроля корректности запроса. Основная цель такого подхода — гарантировать, что до обработчиков маршрутов доходят только структурно корректные данные.
Валидация в Koa обычно охватывает следующие источники данных:
request.body)request.params)request.query)request.headers)Каждый из этих источников требует отдельной схемы проверки, так как их семантика и структура различаются.
Валидация в современных Node.js-приложениях часто строится вокруг JSON Schema — декларативного способа описания структуры данных.
Пример базовой схемы:
{
"type": "object",
"required": ["email", "password"],
"properties": {
"email": {
"type": "string",
"format": "email"
},
"password": {
"type": "string",
"minLength": 8
}
},
"additionalProperties": false
}
Ключевые элементы схемы:
type — определяет тип структурыrequired — список обязательных полейproperties — описание каждого поляadditionalProperties — запрет лишних полейformat — дополнительные ограничения (email, uri и
др.)JSON Schema формирует строгий контракт между клиентом и сервером.
Ajv (Another JSON Schema Validator) — высокопроизводительный валидатор JSON Schema для Node.js и браузера. Он компилирует схемы в оптимизированные функции проверки, что делает его одним из самых быстрых решений в своей категории.
Основные особенности Ajv:
Валидация обычно оформляется как middleware, принимающее схему и
проверяющее ctx.request.body.
import Ajv fr om "ajv";
const ajv = new Ajv({ allErrors: true });
const schema = {
type: "object",
required: ["email", "password"],
properties: {
email: { type: "string", format: "email" },
password: { type: "string", minLength: 8 }
},
additionalProperties: false
};
const validate = ajv.compile(schema);
export function validateBody(ctx, next) {
const valid = validate(ctx.request.body);
if (!valid) {
ctx.status = 400;
ctx.body = {
message: "Validation error",
errors: validate.errors
};
return;
}
return next();
}
Middleware подключается к маршруту:
router.post("/register", validateBody, async (ctx) => {
ctx.body = { status: "ok" };
});
Для масштабируемых приложений создаётся фабрика middleware:
const ajv = new Ajv({ allErrors: true });
function validate(schema) {
const validateFn = ajv.compile(schema);
return async (ctx, next) => {
const valid = validateFn(ctx.request.body);
if (!valid) {
ctx.status = 400;
ctx.body = {
errors: validateFn.errors
};
return;
}
await next();
};
}
Использование:
router.post("/login", validate(loginSchema), loginController);
router.post("/users", validate(userSchema), createUserController);
Параметры URL требуют отдельной схемы:
const paramsSchema = {
type: "object",
required: ["id"],
properties: {
id: { type: "string", pattern: "^[0-9]+$" }
}
};
Middleware:
function validateParams(schema) {
const validateFn = ajv.compile(schema);
return async (ctx, next) => {
const valid = validateFn(ctx.params);
if (!valid) {
ctx.status = 400;
ctx.body = { errors: validateFn.errors };
return;
}
await next();
};
}
Query string часто содержит необязательные поля, поэтому схемы становятся более гибкими:
const querySchema = {
type: "object",
properties: {
page: { type: "integer", minimum: 1, default: 1 },
lim it: { type: "integer", minimum: 1, maximum: 100, default: 10 }
},
additionalProperties: false
};
Особенность работы Ajv — возможность использовать
default значения через опцию useDefaults:
const ajv = new Ajv({ useDefaults: true });
Ajv поддерживает встроенные форматы:
Добавление кастомного формата:
ajv.addFormat("phone", {
type: "string",
validate: (value) => /^\+?[0-9]{10,15}$/.test(value)
});
Использование в схеме:
{
"type": "string",
"format": "phone"
}
Ajv позволяет расширять систему через keywords:
ajv.addKeyword({
keyword: "isOdd",
type: "number",
validate: (schema, data) => {
return schema ? data % 2 === 1 : true;
}
});
Схема:
{
"type": "number",
"isOdd": true
}
Некоторые проверки требуют обращения к базе данных (например, уникальность email).
const schema = {
type: "object",
properties: {
email: {
type: "string",
format: "email",
async: true,
validate: async (email) => {
const user = await db.users.findByEmail(email);
return !user;
}
}
}
};
Ajv поддерживает async-валидацию при использовании
compileAsync:
const validate = await ajv.compileAsync(schema);
const valid = await validate(data);
Ajv возвращает массив ошибок:
[
{
instancePath: "/email",
message: "must match format \"email\"",
keyword: "format",
params: { format: "email" }
}
]
В Koa ошибки обычно нормализуются:
function formatErrors(errors) {
return errors.map(err => ({
field: err.instancePath,
message: err.message
}));
}
При построении API на Koa валидация выполняет роль первого барьера:
Использование Ajv позволяет формализовать этот слой через декларативные схемы, исключая необходимость ручных проверок внутри контроллеров.
В крупных проектах схемы выносятся в отдельную структуру:
/schemas
user.schema.js
auth.schema.js
product.schema.js
Пример модуля:
export const userSchema = {
type: "object",
required: ["name", "email"],
properties: {
name: { type: "string", minLength: 2 },
email: { type: "string", format: "email" }
}
};
Ajv оптимизирует работу за счёт компиляции схем в функции:
Это критично для Koa-приложений с высокой нагрузкой, где валидация выполняется на каждом запросе.
При использовании TypeScript схемы могут быть связаны с типами данных:
interface User {
email: string;
password: string;
}
Схема и тип синхронизируются вручную или через генерацию типов из JSON Schema, что уменьшает расхождения между контрактом и кодом.
В Koa порядок middleware имеет значение:
Нарушение порядка приводит к некорректной обработке данных или отсутствию тела запроса в момент валидации.
app.use(bodyParser());
app.use(validate(userSchema));
app.use(controller);