Javascript indexedDB – Datenbank im Browser

Javascript Session Storage – Daten im Browser speichern

Indexed DB ist eine Datenbank im Browser des Benutzers und komplexer als das WebStorage-API. IndexedDB unterstützt Transaktionen, hat eine theoretisch unbegrenzte Lebenszeit, verkraftet große Datenmengen und ist schnell. Die Operationen der IndexDB sind asynchron, so dass auch aufwändige Transaktionen die Anwendung nicht blockieren.

23-02-02 SITEMAP CSS HTML JS Basis JS Web Tutorial SVG

Keine SQL-Datenbank, aber objektorientiert

Indexed DB ist objektorientiert, ist aber keine relationale Datenbank, hat keine SQL-Unterstützung und nutzt innerhalb seiner Objekte Schlüssel-/Wertpaare. Neben den einfachen Datenbank-Operationen search, get und put unterstützt IndexedDB auch Transaktionen.

Ohne Hilfsprogramm lässt sich die IndexDB-Datenbank nicht mit einer serverbasierten Datenbank synchronisieren, ist also ganz auf die Existenz im Browser angewiesen.

Der Speicher der IndexedDB liegt im GB-Bereich, kann nur vom Benutzer oder der App selbst gelöscht werden, aber im Privaten Modus gibt es keinen Zugriff auf die Datenbank.

indexedDB-Datenbanken sind leistungsfähiger als localStorage und ermöglicht Anwendungen, die sowohl online als auch offline laufen, ist also bestens gerüstet für Anwendungen, die auch ohne Internet-Verbindung funktionieren.

Datenbank-Operationen

Wenn die Datenbank geöffnet wird, können Daten direkt eingegeben werden, meist wird aber erst mal ein ObjectStore erzeugt und die Daten dort eingegeben. Jede Datenbank kann mehrere ObjectStores haben – vergleichbar mit Tabellen in relationalen Datenbanken. Ein ObjectStore könnte z.B. Benutzerdaten mit einer eindeutigen Emailadresse der Index, mit der die Informationen zum Benutzer erreicht werden.

indexedDb objectStore und Index

Die Einträge in einem ObjectStore sind allesamt von derselben Struktur, obwohl einige Einträge extra Eigenschaften aufweisen. Die Kerneigenschaften sind aber immer dieselben.

Ablauf

  1. open: Datenbank öffnen
  2. createObjectStore: Datenbank anlegen
  3. request: Anfrage an die Datenbank in einer Transaktion
  4. request.onsuccess: Abhorchen der Antwort mit einem DOM-Event
  5. Ergebnis des Request-Objekts behandeln

Das kleine Beispiel auf dieser Seite sammelt Farbkombinationen aus einer Primärfarbe und einer Akzentfarbe. Jede Farbkombi bekommt einen Namen – Combo-Name (optional) – und zwei Farben (Vorgabe Schwarz). Bei einem Refresh der Seite oder wenn die Seite geschlossen und später irgendwann wieder geöffnet wird, sind die Farbkombinationen wieder verfügbar.

let request = window.indexedDB.open('colors', 1);
request.onerror = function() {
	console.log('Datenbank konnte nicht geöffnet werden.');
}

request.onsuccess = function() {
	console.log('Datenbankzugriff erfolgt');
	db = request.result;
}

request.onupgradeneeded = function(e) {
	let db = e.target.result;

	let objectStore = db.createObjectStore('colors', { keyPath: 'id', autoIncrement: true});

	objectStore.createIndex('colorCombo', 'colorCombo', { unique: false});
	objectStore.createIndex('favoriteColor', 'favoriteColor', { unique: false});
	objectStore.createIndex('secondColor', 'secondColor', { unique: false});

	console.log('Datenbank eingerichtet');
}

Das indexedDb-API ist ein wenig antiquiert. Viele Beispiele und Tutorials nutzen darum einen Promises Wrapper.

Primary Keys – Primärschlüssel

Wie in jedem Datenbanksystem kann der objectStore einen Schlüssel haben. Der Primary Key identifiziert Daten und kann entweder mittels eines Key Path oder durch einen Schlüsselgenerator erzeugt werden. Der Schlüsselgenerator (Beispiel 2 und 3) erzeugt einen eindeutigen Wert für jedes neu angelegte Objekt.

1 upgradeDb.createObjectStore ("studies", {keyPath: "email"})
2 upgradeDb.createObjectStore ("notes", {autoIncrement: true})
3 upgradeDb.createObjectStore ("logs", {keyPath: "id", autoIncrement: true})

Transaktionen

Eine Transaktion fasst Operationen zusammen um die Integrität der Datenbank abzusichern. Wenn eine Aktion innerhalb einer Transaktion scheitert, wird keine der Aktionen der Transaktion ausgeführt und die Datenbank wieder in den Zustand vor der Transaktion zurück versetzt. Alle Lese- und Schreiboperationen müssen Teil einer Transaktion sein.

objectStore.add(data, optionalKey);

Während sessionStorage und webStorage nur einfache Schlüssel-/Wertpaare aufnehmen, können die Daten einer indexedDB von beliebigem Typ sein: String, Number, Object, Array, JSON und Blobs.

transaction.complete prüft, ob die Operation durchgeführt wurde.

addName.onsubmit = addData;

function addData(e) {
	let newItem = { 
		colorCombo: colorComboInput.value, 
		favoriteColor: favoriteColorInput.value, 
		secondColor: secondColorInput.value
	};

	let transaction = db.transaction(['colors'], 'readwrite');
	let objectStore = transaction.objectStore('colors');
	let request = objectStore.add(newItem);

	request.onsuccess = () => {
		colorComboInput.value = '';
		favoriteColorInput.value = '';
		secondColorInput.value = '';
	};

	transaction.oncomplete = () => {
		console.log('Datenbank-Transaktion durchgeführt');  
		displayData(); 
	}

	transaction.onerror = () => {
		console.log('Datenbank-Transaktion nicht durchgeführt, Fehler!');
	}
}

openCursor()

Einzelne Einträge, deren key bekannt ist, können mit get() gelesen werden. Ein Cursor hingegen läuft alle Werte des ObjectStore.

objectStore.openCursor().onsuccess = function (e) {
	let cursor = e.target.result;
	if (cursor) {
		…
		colorName.textContent = cursor.value.colorCombo;
		cursor.continue();
	} else {
		…
	}
	console.log ("Farben eingespielt");
}

Requisiten: Live-Vorschau und Entwickler-Tools

Für die Entwicklung empfehle ich Visual Studio Code als Programm-Editor, denn VS Code zeigt eine Live-Vorschau ohne Speichern mit dem Plugin "Live Server".

VS Code Live Server
Live Server starten
Shift CMD P Mac
Shift CTRL P PC

Außerdem unersetzlich: die Browser-Console der Entwickler-Tools.

IndexedDB in Entwickler-Tools
Vorzugsweise Chrome, um die Datenbank während der Entwicklung im Blickfeld zu halten.