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

IDBObjectStore.put()

Methode

Speichert einen Datensatz in einem Object Store. Existiert der Schlüssel schon, wird der Datensatz überschrieben, sonst neu angelegt (Upsert).

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

Signatur

IDBObjectStore.prototype.put(value, key)

Beschreibung

put() ist die wichtigste Schreibmethode von IndexedDB. Die Methode arbeitet als Upsert: Gibt es im Store schon einen Datensatz mit demselben Primärschlüssel, wird er vollständig ersetzt. Gibt es keinen, wird ein neuer angelegt. Genau darin liegt der Unterschied zu add(). add() schlägt bei einem schon vorhandenen Schlüssel mit einem ConstraintError fehl.

Wie der Schlüssel bestimmt wird, hängt von der Konfiguration des Stores ab:

  • Mit keyPath (Inline-Schlüssel): Der Schlüssel wird aus dem Objekt selbst gelesen. Den Parameter key darfst du dann nicht angeben.
  • Ohne keyPath (Out-of-line-Schlüssel): Du musst key ausdrücklich übergeben. Die Ausnahme sind Stores mit autoIncrement.
  • Mit autoIncrement: Fehlt der Schlüssel, erzeugt die Datenbank eine fortlaufende Zahl. Bei einem Inline-keyPath wird diese Zahl auch in das Objekt geschrieben.

Der Aufruf läuft asynchron und gibt sofort ein IDBRequest zurück. Bei Erfolg feuert das success-Event, und request.result enthält den Schlüssel des gespeicherten Datensatzes. Dauerhaft gespeichert sind die Daten aber erst, wenn die umgebende Transaktion mit complete abgeschlossen ist. Der Wert wird mit dem Structured-Clone-Algorithmus kopiert. Erlaubt sind also Objekte, Arrays, Date, Blob, Map und Ähnliches, aber keine Funktionen und keine DOM-Knoten.

Parameter

Name Typ Default Beschreibung
$value Pflicht any (strukturiert klonbar) Der zu speichernde Wert. Er wird per Structured Clone kopiert. Hat der Store einen keyPath, muss der Wert ein Objekt sein, das den Schlüssel enthält. Ausnahme: autoIncrement ist aktiv.
$key IDBValidKey (number | string | Date | ArrayBuffer | Array) Primärschlüssel des Datensatzes. Nur für Stores ohne keyPath. Bei einem Inline-Schlüssel führt die Angabe zu einem DataError.

Rückgabewert

Typ
IDBRequest<IDBValidKey>
Beschreibung
Ein IDBRequest-Objekt. Nach dem success-Event enthält request.result den Schlüssel des geschriebenen Datensatzes. Das gilt auch für automatisch erzeugte Schlüssel.

Beispiele

Datensatz anlegen oder aktualisieren (Inline-keyPath)

const openReq = indexedDB.open('shop', 1);

openReq.onupgradeneeded = () => {
  // Store mit Inline-Schlüssel "id"
  openReq.result.createObjectStore('produkte', { keyPath: 'id' });
};

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

  // Erster Aufruf legt an, zweiter überschreibt denselben Schlüssel
  store.put({ id: 42, name: 'Kaffeebecher', preis: 9.9 });
  const req = store.put({ id: 42, name: 'Kaffeebecher', preis: 7.5 });

  req.onsuccess = () => console.log('Gespeichert mit Schlüssel:', req.result);
  tx.oncomplete = () => console.log('Transaktion abgeschlossen');
  tx.onerror = () => console.error('Fehler:', tx.error);
};
Gespeichert mit Schlüssel: 42 Transaktion abgeschlossen

Out-of-line-Schlüssel: Einstellungen als Schlüssel-Wert-Paare

const openReq = indexedDB.open('app', 1);

openReq.onupgradeneeded = () => {
  // Kein keyPath: der Schlüssel wird bei put() mitgegeben
  openReq.result.createObjectStore('einstellungen');
};

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

  store.put('dunkel', 'theme');
  store.put({ lang: 'de', region: 'AT' }, 'locale');

  store.transaction.oncomplete = () => console.log('Einstellungen gespeichert');
};
Einstellungen gespeichert

Promise-Wrapper mit async/await und autoIncrement

const promisify = (req) =>
  new Promise((resolve, reject) => {
    req.onsuccess = () => resolve(req.result);
    req.onerror = () => reject(req.error);
  });

const openDb = () => {
  const req = indexedDB.open('notizen-db', 1);
  req.onupgradeneeded = () =>
    req.result.createObjectStore('notizen', { keyPath: 'id', autoIncrement: true });
  return promisify(req);
};

const speichereNotiz = async (notiz) => {
  const db = await openDb();
  const store = db.transaction('notizen', 'readwrite').objectStore('notizen');
  // Ohne id: neuer Datensatz mit automatisch erzeugtem Schlüssel
  return promisify(store.put({ ...notiz, geaendert: new Date() }));
};

const id = await speichereNotiz({ text: 'Milch kaufen' });
console.log('Neue Notiz-ID:', id);
// Mit id: aktualisiert die bestehende Notiz
await speichereNotiz({ id, text: 'Milch und Brot kaufen' });
console.log('Notiz aktualisiert');
Neue Notiz-ID: 1 Notiz aktualisiert

// Wichtig · Fallstricke

  • Vollständiges Ersetzen: put() überschreibt den ganzen Datensatz. Einzelne Felder werden nicht zusammengeführt. Für eine Teilaktualisierung liest du den Datensatz vorher mit get(), änderst ihn und speicherst ihn wieder. Das geht auch in einem Cursor mit cursor.update(). Beides sollte in derselben Transaktion passieren.
  • Transaktionsmodus: Die Transaktion muss 'readwrite' sein, sonst wird ein ReadOnlyError geworfen. Ist die Transaktion schon beendet, erscheint ein TransactionInactiveError. Das passiert zum Beispiel nach einem await auf ein Promise, das nichts mit IndexedDB zu tun hat, etwa fetch.
  • Fehler beim Schlüssel (DataError): Er tritt auf, wenn key zusammen mit einem Inline-keyPath übergeben wird. Ebenso, wenn weder ein Schlüssel noch autoIncrement vorhanden ist, oder wenn der Schlüssel ungültig ist. Ungültig sind etwa null, undefined, Boolesche Werte und Objekte.
  • DataCloneError: Er tritt auf, wenn der Wert nicht klonbar ist, etwa bei Funktionen, DOM-Knoten oder Proxies. Klassen-Instanzen verlieren beim Speichern ihren Prototyp.
  • Unique-Indizes: Verletzt der neue Wert einen Index mit unique: true, schlägt der Request mit ConstraintError fehl. Standardmäßig bricht das die ganze Transaktion ab, außer du rufst event.preventDefault() auf.
  • Kompatibilität: Die Methode wird in allen modernen Browsern und in Web Workern unterstützt. Im privaten Modus älterer Safari- und Firefox-Versionen war IndexedDB eingeschränkt. Speicherkontingente können einen QuotaExceededError auslösen.