Signatur
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 Parameterkeydarfst du dann nicht angeben. - Ohne
keyPath(Out-of-line-Schlüssel): Du musstkeyausdrücklich übergeben. Die Ausnahme sind Stores mitautoIncrement. - Mit
autoIncrement: Fehlt der Schlüssel, erzeugt die Datenbank eine fortlaufende Zahl. Bei einem Inline-keyPathwird 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
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);
};
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');
};
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');
// 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 mitget(), änderst ihn und speicherst ihn wieder. Das geht auch in einem Cursor mitcursor.update(). Beides sollte in derselben Transaktion passieren. - Transaktionsmodus: Die Transaktion muss
'readwrite'sein, sonst wird einReadOnlyErrorgeworfen. Ist die Transaktion schon beendet, erscheint einTransactionInactiveError. Das passiert zum Beispiel nach einemawaitauf ein Promise, das nichts mit IndexedDB zu tun hat, etwafetch. - Fehler beim Schlüssel (
DataError): Er tritt auf, wennkeyzusammen mit einem Inline-keyPathübergeben wird. Ebenso, wenn weder ein Schlüssel nochautoIncrementvorhanden ist, oder wenn der Schlüssel ungültig ist. Ungültig sind etwanull,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 mitConstraintErrorfehl. Standardmäßig bricht das die ganze Transaktion ab, außer du rufstevent.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
QuotaExceededErrorauslösen.