Операция добавления данных в Dexie.js реализуется через метод
add(), который предоставляет высокоуровневый интерфейс для
вставки новой записи в таблицу IndexedDB. В отличие от низкоуровневого
API IndexedDB, где требуется создание запросов, обработка событий успеха
и ошибок, Dexie.js инкапсулирует весь процесс в один вызов, возвращающий
Promise.
Метод add() работает строго в контексте таблицы, то есть
вызывается на экземпляре Table:
db.users.add({ name: "Alex", age: 25 });
Результатом выполнения является Promise, который резолвится в первичный ключ добавленной записи.
Основная сигнатура метода:
table.add(item, [key])
item — объект, который будет сохранён в таблицеkey — необязательный параметр, задающий значение
первичного ключа вручную (если схема позволяет)Возвращаемое значение:
Promise<PrimaryKey> — ключ добавленной
записиПример:
const id = await db.users.add({
name: "Maria",
age: 31
});
console.log(id);
Если таблица использует автоинкрементный ключ, Dexie автоматически сгенерирует его.
Dexie.js опирается на схему таблицы, заданную через
stores(). Например:
const db = new Dexie("AppDB");
db.version(1).stores({
users: "++id, name, age"
});
Здесь:
++id означает автоинкрементный первичный ключname, age — индексируемые поляПри добавлении записи:
await db.users.add({
name: "Ivan",
age: 40
});
поле id будет сгенерировано автоматически.
Если же ключ передан явно:
await db.users.add(
{ name: "Ivan", age: 40 },
1001
);
Dexie попытается использовать 1001 как primary key. При
конфликте возникнет ошибка.
Одна из ключевых особенностей add() — строгое
предотвращение перезаписи существующих записей.
Если ключ уже существует, операция завершится ошибкой:
Dexie.ConstraintError: Key already exists in the object store
Пример:
await db.users.add({ id: 1, name: "A" });
await db.users.add({ id: 1, name: "B" }); // ошибка
Это принципиальное отличие от метода put(), который
выполняет вставку или обновление.
Хотя оба метода используются для записи данных, их семантика различается:
add() — только вставка, без перезаписиput() — вставка или обновлениеawait db.users.add({ id: 1, name: "Alex" }); // ошибка при повторе
await db.users.put({ id: 1, name: "Alex" }); // безопасно обновит
add() применяется в случаях, где требуется гарантия
уникальности записи и строгая целостность данных.
Метод возвращает Promise, что позволяет использовать
async/await:
async function createUser() {
const id = await db.users.add({
name: "Olga",
age: 28
});
return id;
}
При использовании цепочек:
db.users.add({ name: "Pavel" })
.then(id => console.log("Created:", id))
.catch(err => console.error(err));
Ошибки обрабатываются стандартным механизмом Promise rejection.
Dexie позволяет выполнять add() внутри транзакций, что
обеспечивает атомарность операций.
await db.transaction("rw", db.users, async () => {
await db.users.add({ name: "User1" });
await db.users.add({ name: "User2" });
});
Если одна из операций завершится ошибкой, вся транзакция будет откатана.
Это особенно важно при массовой вставке связанных данных.
При вызове add() Dexie не только сохраняет объект, но и
обновляет все индексированные поля, определённые в схеме:
db.version(1).stores({
users: "++id, email, age"
});
Добавление:
await db.users.add({
email: "test@example.com",
age: 22
});
После вставки:
email обновляетсяage обновляетсяВажно, что IndexedDB требует, чтобы индексированные поля существовали
в объекте, иначе они будут сохранены как undefined, что
может повлиять на поиск.
Метод имеет ряд ограничений, обусловленных как IndexedDB, так и реализацией Dexie:
Пример некорректного использования:
await db.users.add({ name: "NoAgeUser" });
// если age требуется логикой приложения — это приведёт к неполным данным
При использовании ++primaryKey Dexie делегирует
генерацию ключа IndexedDB:
db.version(1).stores({
logs: "++id, message"
});
Добавление:
await db.logs.add({ message: "System started" });
Происходит:
idЭтот механизм гарантирует уникальность даже при параллельных вставках.
Возвращаемое значение add() — это первичный ключ,
который можно использовать сразу после вставки:
const userId = await db.users.add({
name: "Sergey"
});
const user = await db.users.get(userId);
Это позволяет строить цепочки зависимых операций без дополнительного поиска.
Хотя add() предназначен для одиночной вставки, при
большом количестве данных его использование становится
неэффективным.
await db.users.add({ name: "A" });
await db.users.add({ name: "B" });
await db.users.add({ name: "C" });
Dexie предоставляет специализированный метод bulkAdd(),
оптимизированный для пакетной вставки, который снижает количество
транзакционных операций.
Тем не менее add() остаётся базовым строительным блоком
для единичных операций.
Основные ошибки при использовании:
ConstraintError — нарушение уникальности ключаDataError — некорректный формат данныхTransactionInactiveError — попытка записи вне активной
транзакцииInvalidStateError — некорректное состояние базыПример обработки:
try {
await db.users.add({ id: 1, name: "Test" });
} catch (e) {
if (e.name === "ConstraintError") {
console.log("Запись с таким ключом уже существует");
}
}
Dexie использует structured clone algorithm, что означает:
Пример:
await db.users.add({
name: "Anna",
profile: {
city: "Almaty",
active: true
}
});
Такая структура будет корректно сохранена в IndexedDB.
Dexie не требует строгой схемы объектов, но индексы должны быть заранее определены.
Если поле не индексировано, оно всё равно сохраняется, но не участвует в поиске:
db.version(1).stores({
users: "++id, name"
});
await db.users.add({
name: "Dmitry",
metadata: { role: "admin" }
});
Поле metadata будет сохранено, но не индексировано.
Dexie управляет конкурентным доступом к IndexedDB автоматически. При одновременных вызовах:
db.users.add({ name: "A" });
db.users.add({ name: "B" });
db.users.add({ name: "C" });
операции будут сериализованы внутри транзакции браузера, сохраняя целостность данных.