Хук creating

Хук creating в Dexie.js перехватывает момент добавления новой записи в таблицу до фактической записи данных в IndexedDB. Он выполняется синхронно или асинхронно (в зависимости от реализации обработчика) и позволяет модифицировать объект, запретить вставку или дополнить данные служебными полями. Это один из ключевых механизмов управления целостностью данных на уровне ORM-слоя.

Хук creating регистрируется на уровне таблицы:

db.users.hook('creating', function (primKey, obj, trans) {
    // логика
});

Параметры обработчика:

  • primKey — первичный ключ создаваемой записи (если он задан вручную или генерируется заранее)
  • obj — объект данных, который будет сохранён
  • trans — текущая транзакция Dexie

Возвращаемое значение хука может влиять на первичный ключ или модификацию объекта в зависимости от логики обработки.

Порядок выполнения и место в жизненном цикле

creating вызывается строго до записи в IndexedDB, но после начала транзакции. Последовательность выглядит следующим образом:

  1. Начало транзакции
  2. Вызов хука creating
  3. Применение возможных изменений объекта
  4. Запись в object store
  5. Завершение транзакции

Это делает хук подходящим для:

  • нормализации данных
  • добавления метаданных
  • валидации перед записью
  • генерации ключей

Изменение объекта данных

Одной из ключевых особенностей является возможность мутировать объект напрямую:

db.users.hook('creating', function (primKey, obj, trans) {
    obj.createdAt = Date.now();
    obj.isActive = true;
});

В этом случае изменения будут сохранены вместе с записью без дополнительного кода в бизнес-логике.

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

Управление первичным ключом

Хук позволяет влиять на значение первичного ключа, возвращая новое значение:

db.orders.hook('creating', function (primKey, obj, trans) {
    if (!primKey) {
        return crypto.randomUUID();
    }
});

Если возвращается значение:

  • оно становится новым primary key
  • оригинальный primKey игнорируется

Это полезно при кастомной генерации идентификаторов вне IndexedDB auto-increment.

Асинхронная логика и Promise

Dexie поддерживает асинхронные хуки через возвращение Promise. Это позволяет выполнять внешние операции перед сохранением:

db.users.hook('creating', async function (primKey, obj, trans) {
    const profile = await fetch(`/api/profile/${obj.id}`).then(r => r.json());
    obj.profileSnapshot = profile;
});

Особенности:

  • транзакция ожидает завершения Promise
  • запись не произойдёт до завершения асинхронной логики
  • ошибка в Promise отменяет вставку

Контекст транзакции

Третий аргумент trans даёт доступ к текущей транзакции Dexie:

db.logs.hook('creating', function (primKey, obj, trans) {
    trans.on('complete', () => {
        console.log('Запись завершена');
    });
});

Через транзакцию можно:

  • подписываться на события complete, error, abort
  • выполнять дополнительные операции в рамках той же транзакции
  • синхронизировать связанные изменения

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

creating часто используется как слой валидации:

db.users.hook('creating', function (primKey, obj) {
    if (!obj.email) {
        throw new Error('Email обязателен');
    }

    if (obj.age < 0) {
        throw new Error('Некорректный возраст');
    }
});

При выбрасывании исключения:

  • операция вставки отменяется
  • транзакция может быть прервана
  • ошибка передаётся в вызывающий код

Нормализация структуры

Хук удобен для приведения данных к единому формату:

db.messages.hook('creating', function (primKey, obj) {
    obj.text = obj.text.trim();
    obj.createdAt = obj.createdAt || Date.now();
    obj.tags = Array.isArray(obj.tags) ? obj.tags : [];
});

Это снижает необходимость предварительной обработки на уровне UI или сервисного слоя.

Побочные эффекты и ограничения

Несмотря на гибкость, хук имеет ограничения:

  • нельзя выполнять долгие блокирующие операции без Promise
  • нельзя полагаться на DOM или UI-состояние
  • нельзя изменять уже записанные данные (только до вставки)
  • порядок выполнения нескольких хуков может быть критичен

Если зарегистрировано несколько хуков creating, они выполняются последовательно в порядке добавления.

Работа с несколькими хранилищами

Хуки действуют строго на уровне конкретной таблицы:

db.users.hook('creating', fn);
db.orders.hook('creating', fn);

Логика не пересекается между таблицами, даже если структуры объектов похожи.

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

Типичный сценарий — добавление audit-полей:

db.audit.hook('creating', function (primKey, obj) {
    obj.createdAt = new Date().toISOString();
    obj.createdBy = 'system';
});

При необходимости можно использовать данные транзакции или внешнего контекста приложения для заполнения этих полей.

Комбинация с другими хуками

creating часто используется вместе с:

  • updating — для модификации при обновлении
  • deleting — для контроля удаления
  • reading — для трансформации данных при чтении

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