Start · Sprachen · JavaScript · Referenz · IDBObjectStore.add()

IDBObjectStore.add()

Methode

Fügt einen neuen Datensatz als strukturierten Klon in den Objekt-Store ein und schlägt fehl, wenn der Schlüssel bereits existiert.

seit JavaScript Baseline: alle Browser (IndexedDB 2.0) Kategorie: webapi

Signatur

IDBObjectStore.prototype.add(value, key)

Beschreibung

add() legt einen neuen Datensatz in einem IDBObjectStore an. Der übergebene Wert wird per Structured-Clone-Algorithmus kopiert und asynchron gespeichert. Zurückgegeben wird sofort ein IDBRequest. Dessen success-Ereignis liefert in request.result den Schlüssel des neuen Datensatzes.

Anders als put() überschreibt add() niemals etwas: Gibt es den Schlüssel im Store schon, scheitert die Anfrage mit einem ConstraintError. Das Gleiche passiert, wenn ein Index mit unique: true verletzt wird. Nimm add() also immer dann, wenn doppelte Einträge ein Fehler sind, etwa bei Registrierungen, Importen oder eindeutigen Kennungen.

Wie der Schlüssel bestimmt wird, hängt vom Store ab:

  • Mit keyPath (Inline-Schlüssel): Der Schlüssel steht im Objekt selbst. Das Argument key darf dann nicht übergeben werden.
  • Ohne keyPath (Out-of-line-Schlüssel): Der Schlüssel muss als zweites Argument übergeben werden, außer der Store nutzt autoIncrement.
  • Mit autoIncrement: Fehlt ein Schlüssel, erzeugt der Key-Generator eine fortlaufende Nummer. Bei einem keyPath wird sie zusätzlich in das Objekt geschrieben.

Die Methode funktioniert nur innerhalb einer Transaktion im Modus "readwrite". Ein Fehler bei einer einzelnen Anfrage bricht standardmäßig die ganze Transaktion ab. Das lässt sich im error-Handler mit event.preventDefault() verhindern.

Parameter

Name Typ Default Beschreibung
$value Pflicht any (strukturiert klonbar) Der zu speichernde Wert. Er muss sich mit dem Structured-Clone-Algorithmus kopieren lassen: Objekte, Arrays, Date, Map, Set, Blob, File, ArrayBuffer usw. Funktionen und DOM-Knoten gehen nicht.
$key IDBValidKey (number | string | Date | ArrayBuffer | Array) Schlüssel des Datensatzes. Nur für Stores ohne keyPath erlaubt. Pflicht, wenn der Store weder keyPath noch autoIncrement hat.

Rückgabewert

Typ
IDBRequest<IDBValidKey>
Beschreibung
Ein IDBRequest. Nach dem success-Ereignis enthält result den Schlüssel des neu angelegten Datensatzes. Schlägt die Anfrage fehl, enthält error eine DOMException, z. B. ConstraintError.

Beispiele

Datensatz mit autoIncrement anlegen

const openReq = indexedDB.open('notizen-db', 1);

openReq.onupgradeneeded = () => {
  const db = openReq.result;
  db.createObjectStore('notizen', { keyPath: 'id', autoIncrement: true });
};

openReq.onsuccess = () => {
  const db = openReq.result;
  const tx = db.transaction('notizen', 'readwrite');
  const store = tx.objectStore('notizen');

  const req = store.add({ titel: 'Einkaufen', erstellt: new Date() });
  req.onsuccess = () => console.log('Neue ID:', req.result);

  tx.oncomplete = () => console.log('Transaktion abgeschlossen');
};
Neue ID: 1 Transaktion abgeschlossen

Doppelte Schlüssel abfangen (ConstraintError)

// Store 'nutzer' mit keyPath 'email'
const nutzerAnlegen = (db, nutzer) => {
  const tx = db.transaction('nutzer', 'readwrite');
  const req = tx.objectStore('nutzer').add(nutzer);

  req.onerror = (event) => {
    if (req.error.name === 'ConstraintError') {
      console.warn(`E-Mail ${nutzer.email} ist bereits registriert`);
      event.preventDefault(); // Transaktion nicht abbrechen
    }
  };
  req.onsuccess = () => console.log('Angelegt:', req.result);
};

// nutzerAnlegen(db, { email: 'anna@example.com', name: 'Anna' });
// nutzerAnlegen(db, { email: 'anna@example.com', name: 'Anna B.' });
Angelegt: anna@example.com E-Mail anna@example.com ist bereits registriert

Promise-Wrapper mit Out-of-line-Schlüssel

const addAsync = (db, storeName, value, key) =>
  new Promise((resolve, reject) => {
    const tx = db.transaction(storeName, 'readwrite');
    const req = tx.objectStore(storeName).add(value, key);
    req.onsuccess = () => resolve(req.result);
    req.onerror = () => reject(req.error);
  });

// Store 'einstellungen' ohne keyPath, Schlüssel wird separat übergeben
const speichern = async (db) => {
  try {
    const key = await addAsync(db, 'einstellungen', { dunkel: true }, 'theme');
    console.log('Gespeichert unter', key);
  } catch (err) {
    console.error(err.name, err.message);
  }
};
Gespeichert unter theme

// Wichtig · Fallstricke

  • add() oder put(): add() scheitert, wenn der Schlüssel schon existiert. Für „einfügen oder aktualisieren“ nimm put().
  • Synchron geworfene Ausnahmen, also noch vor dem Request:
    • ReadOnlyError: Die Transaktion ist nicht "readwrite".
    • TransactionInactiveError: Die Transaktion ist schon beendet, oft weil nach einem await auf etwas anderes als eine IndexedDB-Anfrage weitergearbeitet wurde.
    • DataError: Der Schlüssel ist ungültig, fehlt, obwohl er Pflicht ist, oder wurde trotz keyPath übergeben.
    • DataCloneError: Der Wert lässt sich nicht klonen, z. B. weil er Funktionen enthält.
    • InvalidStateError: Der Store wurde gelöscht.
  • Transaktions-Lebensdauer: Zwischen Anfragen derselben Transaktion keine fremden asynchronen Operationen wie fetch oder setTimeout einschieben. Sonst schließt sich die Transaktion automatisch.
  • Fehler brechen die Transaktion ab: Ein nicht behandelter ConstraintError macht alle Änderungen der Transaktion rückgängig. Das ist beim Massenimport wichtig.
  • Speicher: Wird das Kontingent überschritten, entsteht ein QuotaExceededError. Im privaten Modus mancher Browser ist IndexedDB eingeschränkt.
  • IndexedDB wird von allen modernen Browsern und in Web Workern unterstützt, nicht jedoch in Node.js ohne Polyfill (z. B. fake-indexeddb).