Joi библиотека

Библиотека Joi представляет собой инструмент декларативной валидации данных, ориентированный на описание схем и проверку соответствия входящих значений заданным правилам. Она широко применяется в серверных приложениях Node.js, особенно в связке с REST API, где требуется строгий контроль структуры входящих запросов.


Основные принципы работы Joi

Joi опирается на концепцию схем (schema-based validation), где каждое значение описывается через набор правил. Схема определяет:

  • тип данных
  • ограничения (минимум, максимум, длина)
  • формат
  • обязательность
  • дополнительные условия

Валидация выполняется путём сопоставления входных данных со схемой.


Установка и подключение

Библиотека устанавливается через npm:

npm install joi

Использование в коде:

const Joi = require('joi');

В современных версиях также поддерживается ESM-синтаксис:

import Joi from 'joi';

Базовые типы схем

Строки

const schema = Joi.string();

Расширенные ограничения:

const schema = Joi.string()
  .min(3)
  .max(30)
  .required();

Дополнительные проверки:

Joi.string().email();
Joi.string().uri();
Joi.string().pattern(/^[a-z]+$/);

Числа

const schema = Joi.number();

Ограничения:

Joi.number().min(0).max(100);
Joi.number().integer();
Joi.number().positive();

Булевы значения

Joi.boolean();

Массивы

const schema = Joi.array();

Содержимое массива:

Joi.array().items(Joi.string());

Ограничения:

Joi.array().min(1).max(5);
Joi.array().unique();

Объекты

Наиболее часто используемый тип в Joi.

const schema = Joi.object({
  name: Joi.string().required(),
  age: Joi.number().min(18)
});

Валидация данных

Синхронная валидация

const schema = Joi.object({
  username: Joi.string().min(3).required()
});

const result = schema.validate({ username: 'alex' });

Результат содержит:

  • value — преобразованные данные
  • error — объект ошибки при несоответствии

Асинхронная валидация

const result = await schema.validateAsync(data);

При ошибке выбрасывается исключение.


Поведение ошибок

Joi формирует структурированные ошибки, содержащие:

  • message — текст ошибки
  • path — путь к полю
  • type — тип нарушения правила

Пример:

{
  message: '"username" is required',
  path: ['username'],
  type: 'any.required'
}

Настройки валидации

abortEarly

По умолчанию Joi прекращает проверку после первой ошибки. Для получения полного списка ошибок:

schema.validate(data, { abortEarly: false });

allowUnknown

Разрешает наличие дополнительных полей:

Joi.object().unknown(true);

stripUnknown

Удаляет неизвестные поля:

Joi.object({
  name: Joi.string()
}).unknown(false).options({ stripUnknown: true });

Кастомизация сообщений

Joi.string().min(3).messages({
  'string.min': 'Слишком короткая строка'
});

Сообщения могут быть привязаны к конкретным типам ошибок.


Условная валидация

when

Позволяет изменять правила в зависимости от значения другого поля.

const schema = Joi.object({
  role: Joi.string().valid('admin', 'user'),
  access: Joi.string().when('role', {
    is: 'admin',
    then: Joi.valid('all'),
    otherwise: Joi.valid('limited')
  })
});

Альтернативные схемы

alternatives

const schema = Joi.alternatives().try(
  Joi.string(),
  Joi.number()
);

Используется для поддержки нескольких допустимых типов.


Кастомные правила

const schema = Joi.string().custom((value, helpers) => {
  if (value === 'forbidden') {
    return helpers.error('any.invalid');
  }
  return value;
});

Композиция схем

Joi позволяет комбинировать схемы:

const base = Joi.string().min(3);

const extended = base.max(10).required();

Работа с вложенными объектами

const schema = Joi.object({
  user: Joi.object({
    name: Joi.string(),
    age: Joi.number()
  })
});

Валидация массивов объектов

const schema = Joi.array().items(
  Joi.object({
    id: Joi.number(),
    title: Joi.string()
  })
);

Значения по умолчанию

const schema = Joi.object({
  role: Joi.string().default('user')
});

При отсутствии поля значение автоматически подставляется.


Приведение типов

Joi способен преобразовывать входные данные:

Joi.number().integer().convert();

Пример: строка “123” может быть преобразована в число.


Строгий режим

Joi.object().strict();

Отключает автоматическое приведение типов.


Проверка наличия обязательных полей

Joi.object({
  email: Joi.string().required()
});

Комплексные схемы API-валидации

Типичная структура запроса:

const schema = Joi.object({
  body: Joi.object({
    username: Joi.string().min(3).required(),
    password: Joi.string().min(6).required()
  }),
  query: Joi.object({
    page: Joi.number().default(1)
  })
});

Переиспользование схем

Схемы могут быть вынесены и повторно использованы:

const userSchema = Joi.object({
  name: Joi.string(),
  age: Joi.number()
});

const createUserSchema = userSchema.required();

Расширение Joi

Поддерживается создание собственных расширений:

const customJoi = Joi.extend((joi) => ({
  type: 'evenNumber',
  base: joi.number(),
  validate(value, helpers) {
    if (value % 2 !== 0) {
      return { value, errors: helpers.error('number.even') };
    }
  }
}));

Сравнение и зависимости между полями

Joi.object({
  password: Joi.string(),
  confirmPassword: Joi.string().valid(Joi.ref('password'))
});

Работа с null и optional значениями

Joi.string().allow(null);
Joi.string().optional();

Преобразование структуры результата

const schema = Joi.object({
  name: Joi.string().required()
}).prefs({ convert: true });

Вложенные массивы и сложные структуры

const schema = Joi.object({
  users: Joi.array().items(
    Joi.object({
      id: Joi.number(),
      tags: Joi.array().items(Joi.string())
    })
  )
});

Поведение при ошибках в глубоко вложенных структурах

Ошибки возвращаются с полным путём:

  • user.name
  • users[0].tags[1]

Это позволяет точно локализовать проблему в данных.


Использование ref-ссылок

const schema = Joi.object({
  min: Joi.number(),
  max: Joi.number().min(Joi.ref('min'))
});

Работа с датами

Joi.date();
Joi.date().iso();
Joi.date().greater('2020-01-01');

Схемы с логикой состояния

Joi.object({
  isActive: Joi.boolean(),
  lastLogin: Joi.date().when('isActive', {
    is: true,
    then: Joi.required()
  })
});

Производительность и особенности применения

Joi оптимизирован для серверной валидации и используется в сценариях:

  • проверка входных данных API
  • валидация конфигураций
  • контроль структуры payload
  • фильтрация пользовательского ввода

Сложные схемы могут влиять на производительность при глубокой вложенности и большом объёме данных, что требует структурного проектирования схем.


Принципы построения устойчивых схем

Используются подходы:

  • разделение схем по слоям (body, query, params)
  • переиспользование базовых схем
  • минимизация дублирования правил
  • централизованное описание типов данных