Daten im Web lesen und schreiben

(Optional) Erstellen Sie einen Prototyp und testen Sie ihn mit der Firebase Local Emulator Suite

Bevor wir darüber sprechen, wie Ihre App aus der Realtime Database liest und in sie schreibt, stellen wir Ihnen eine Reihe von Tools vor, die Sie zum Prototyping und Testen der Realtime Database-Funktionalität verwenden können: Firebase Local Emulator Suite. Wenn Sie verschiedene Datenmodelle ausprobieren, Ihre Sicherheitsregeln optimieren oder daran arbeiten, den kostengünstigsten Weg zur Interaktion mit dem Back-End zu finden, kann es eine gute Idee sein, lokal arbeiten zu können, ohne Live-Dienste bereitzustellen.

Ein Realtime Database-Emulator ist Teil der Local Emulator Suite, die es Ihrer App ermöglicht, mit Ihren emulierten Datenbankinhalten und -konfigurationen sowie optional mit Ihren emulierten Projektressourcen (Funktionen, andere Datenbanken und Sicherheitsregeln) zu interagieren.

Die Verwendung des Realtime Database-Emulators umfasst nur wenige Schritte:

  1. Hinzufügen einer Codezeile zur Testkonfiguration Ihrer App, um eine Verbindung mit dem Emulator herzustellen.
  2. Führen Sie im Stammverzeichnis Ihres lokalen Projektverzeichnisses firebase emulators:start .
  3. Aufrufe aus dem Prototypcode Ihrer App wie gewohnt mit einem SDK der Realtime Database-Plattform oder mit der REST-API der Realtime Database.

Eine detaillierte Anleitung zu Realtime Database und Cloud Functions ist verfügbar. Sie sollten sich auch die Einführung in die Local Emulator Suite ansehen .

Holen Sie sich eine Datenbankreferenz

Um Daten aus der Datenbank zu lesen oder zu schreiben, benötigen Sie eine Instanz von firebase.database.Reference :

Web version 9

import { getDatabase } from "firebase/database";

const database = getDatabase();

Web version 8

var database = firebase.database();

Daten schreiben

Dieses Dokument behandelt die Grundlagen zum Abrufen von Daten und zum Sortieren und Filtern von Firebase-Daten.

Firebase-Daten werden abgerufen, indem ein asynchroner Listener an eine firebase.database.Reference angehängt wird. Der Listener wird einmal für den Anfangszustand der Daten ausgelöst und erneut, wenn sich die Daten ändern.

Grundlegende Schreiboperationen

Für grundlegende Schreiboperationen können Sie set() verwenden, um Daten in einer angegebenen Referenz zu speichern und alle vorhandenen Daten in diesem Pfad zu ersetzen. Beispielsweise könnte eine Social-Blogging-Anwendung einen Benutzer mit set() wie folgt hinzufügen:

Web version 9

import { getDatabase, ref, set } from "firebase/database";

function writeUserData(userId, name, email, imageUrl) {
  const db = getDatabase();
  set(ref(db, 'users/' + userId), {
    username: name,
    email: email,
    profile_picture : imageUrl
  });
}

Web version 8

function writeUserData(userId, name, email, imageUrl) {
  firebase.database().ref('users/' + userId).set({
    username: name,
    email: email,
    profile_picture : imageUrl
  });
}

Die Verwendung von set() überschreibt Daten an der angegebenen Position, einschließlich aller untergeordneten Knoten.

Daten lesen

Achten Sie auf Wertereignisse

Um Daten an einem Pfad zu lesen und auf Änderungen zu lauschen, verwenden Sie onValue() , um Ereignisse zu beobachten. Sie können dieses Ereignis verwenden, um statische Snapshots der Inhalte in einem bestimmten Pfad zu lesen, wie sie zum Zeitpunkt des Ereignisses vorhanden waren. Diese Methode wird einmal ausgelöst, wenn der Listener angehängt wird, und jedes Mal, wenn sich die Daten, einschließlich der untergeordneten Elemente, ändern. Dem Ereignis-Callback wird ein Snapshot übergeben, der alle Daten an diesem Speicherort enthält, einschließlich untergeordneter Daten. Wenn keine Daten vorhanden sind, gibt der Snapshot false zurück, wenn Sie exists() aufrufen, und null , wenn Sie val() darauf aufrufen.

Das folgende Beispiel zeigt eine Social-Blogging-Anwendung, die die Anzahl der Sterne eines Beitrags aus der Datenbank abruft:

Web version 9

import { getDatabase, ref, onValue} from "firebase/database";

const db = getDatabase();
const starCountRef = ref(db, 'posts/' + postId + '/starCount');
onValue(starCountRef, (snapshot) => {
  const data = snapshot.val();
  updateStarCount(postElement, data);
});

Web version 8

var starCountRef = firebase.database().ref('posts/' + postId + '/starCount');
starCountRef.on('value', (snapshot) => {
  const data = snapshot.val();
  updateStarCount(postElement, data);
});

Der Listener erhält einen snapshot , der die Daten an der angegebenen Position in der Datenbank zum Zeitpunkt des Ereignisses enthält. Sie können die Daten im snapshot mit der Methode val() abrufen.

Daten einmal lesen

Daten einmal mit get() lesen

Das SDK wurde entwickelt, um Interaktionen mit Datenbankservern zu verwalten, unabhängig davon, ob Ihre App online oder offline ist.

Im Allgemeinen sollten Sie die oben beschriebenen Wertereignistechniken verwenden, um Daten zu lesen, um vom Back-End über Aktualisierungen der Daten benachrichtigt zu werden. Die Listener-Techniken reduzieren Ihre Nutzung und Abrechnung und sind optimiert, um Ihren Benutzern das beste Erlebnis zu bieten, wenn sie online und offline gehen.

Wenn Sie die Daten nur einmal benötigen, können Sie get() einen Snapshot der Daten aus der Datenbank abrufen. Wenn get() aus irgendeinem Grund den Serverwert nicht zurückgeben kann, prüft der Client den lokalen Speichercache und gibt einen Fehler zurück, wenn der Wert immer noch nicht gefunden wird.

Die unnötige Verwendung von get() kann die Nutzung der Bandbreite erhöhen und zu Leistungsverlusten führen, was durch die Verwendung eines Echtzeit-Listeners wie oben gezeigt verhindert werden kann.

Web version 9

import { getDatabase, ref, child, get } from "firebase/database";

const dbRef = ref(getDatabase());
get(child(dbRef, `users/${userId}`)).then((snapshot) => {
  if (snapshot.exists()) {
    console.log(snapshot.val());
  } else {
    console.log("No data available");
  }
}).catch((error) => {
  console.error(error);
});

Web version 8

const dbRef = firebase.database().ref();
dbRef.child("users").child(userId).get().then((snapshot) => {
  if (snapshot.exists()) {
    console.log(snapshot.val());
  } else {
    console.log("No data available");
  }
}).catch((error) => {
  console.error(error);
});

Lesen Sie die Daten einmal mit einem Beobachter ab

In einigen Fällen möchten Sie möglicherweise, dass der Wert aus dem lokalen Cache sofort zurückgegeben wird, anstatt auf dem Server nach einem aktualisierten Wert zu suchen. In diesen Fällen können Sie once() verwenden, um die Daten sofort aus dem lokalen Festplattencache zu holen.

Dies ist nützlich für Daten, die nur einmal geladen werden müssen und sich voraussichtlich nicht häufig ändern oder aktives Zuhören erfordern. Beispielsweise verwendet die Blogging-App in den vorherigen Beispielen diese Methode, um das Profil eines Benutzers zu laden, wenn er mit dem Verfassen eines neuen Beitrags beginnt:

Web version 9

import { getDatabase, ref, onValue } from "firebase/database";
import { getAuth } from "firebase/auth";

const db = getDatabase();
const auth = getAuth();

const userId = auth.currentUser.uid;
return onValue(ref(db, '/users/' + userId), (snapshot) => {
  const username = (snapshot.val() && snapshot.val().username) || 'Anonymous';
  // ...
}, {
  onlyOnce: true
});

Web version 8

var userId = firebase.auth().currentUser.uid;
return firebase.database().ref('/users/' + userId).once('value').then((snapshot) => {
  var username = (snapshot.val() && snapshot.val().username) || 'Anonymous';
  // ...
});

Aktualisieren oder Löschen von Daten

Aktualisieren Sie bestimmte Felder

Um gleichzeitig in bestimmte untergeordnete Knoten eines Knotens zu schreiben, ohne andere untergeordnete Knoten zu überschreiben, verwenden Sie die Methode update() .

Beim Aufrufen von update() können Sie untergeordnete Werte auf niedrigerer Ebene aktualisieren, indem Sie einen Pfad für den Schlüssel angeben. Wenn Daten zur besseren Skalierung an mehreren Speicherorten gespeichert werden, können Sie alle Instanzen dieser Daten mithilfe von Datenauffächerung aktualisieren.

Beispielsweise könnte eine Social-Blogging-App einen Beitrag erstellen und ihn gleichzeitig mit Code wie dem folgenden auf den Feed der letzten Aktivitäten und den Aktivitätsfeed des postenden Benutzers aktualisieren:

Web version 9

import { getDatabase, ref, child, push, update } from "firebase/database";

function writeNewPost(uid, username, picture, title, body) {
  const db = getDatabase();

  // A post entry.
  const postData = {
    author: username,
    uid: uid,
    body: body,
    title: title,
    starCount: 0,
    authorPic: picture
  };

  // Get a key for a new Post.
  const newPostKey = push(child(ref(db), 'posts')).key;

  // Write the new post's data simultaneously in the posts list and the user's post list.
  const updates = {};
  updates['/posts/' + newPostKey] = postData;
  updates['/user-posts/' + uid + '/' + newPostKey] = postData;

  return update(ref(db), updates);
}

Web version 8

function writeNewPost(uid, username, picture, title, body) {
  // A post entry.
  var postData = {
    author: username,
    uid: uid,
    body: body,
    title: title,
    starCount: 0,
    authorPic: picture
  };

  // Get a key for a new Post.
  var newPostKey = firebase.database().ref().child('posts').push().key;

  // Write the new post's data simultaneously in the posts list and the user's post list.
  var updates = {};
  updates['/posts/' + newPostKey] = postData;
  updates['/user-posts/' + uid + '/' + newPostKey] = postData;

  return firebase.database().ref().update(updates);
}

In diesem Beispiel wird push() verwendet, um einen Beitrag im Knoten zu erstellen, der Beiträge für alle Benutzer unter /posts/$postid enthält, und gleichzeitig den Schlüssel abzurufen. Der Schlüssel kann dann verwendet werden, um einen zweiten Eintrag in den Beiträgen des Benutzers unter /user-posts/$userid/$postid zu erstellen.

Mithilfe dieser Pfade können Sie mit einem einzigen Aufruf von update() simultane Aktualisierungen an mehreren Stellen in der JSON-Struktur durchführen, z. B. wie in diesem Beispiel der neue Beitrag an beiden Stellen erstellt wird. Auf diese Weise durchgeführte gleichzeitige Aktualisierungen sind atomar: Entweder sind alle Aktualisierungen erfolgreich oder alle Aktualisierungen schlagen fehl.

Fügen Sie einen Abschluss-Callback hinzu

Wenn Sie wissen möchten, wann Ihre Daten festgeschrieben wurden, können Sie einen Abschluss-Callback hinzufügen. Sowohl set() als auch update() nehmen einen optionalen Abschluss-Callback entgegen, der aufgerufen wird, wenn der Schreibvorgang an die Datenbank übertragen wurde. Wenn der Aufruf nicht erfolgreich war, wird dem Rückruf ein Fehlerobjekt übergeben, das angibt, warum der Fehler aufgetreten ist.

Web version 9

import { getDatabase, ref, set } from "firebase/database";

const db = getDatabase();
set(ref(db, 'users/' + userId), {
  username: name,
  email: email,
  profile_picture : imageUrl
})
.then(() => {
  // Data saved successfully!
})
.catch((error) => {
  // The write failed...
});

Web version 8

firebase.database().ref('users/' + userId).set({
  username: name,
  email: email,
  profile_picture : imageUrl
}, (error) => {
  if (error) {
    // The write failed...
  } else {
    // Data saved successfully!
  }
});

Daten löschen

Der einfachste Weg, Daten zu löschen, besteht darin remove() für einen Verweis auf den Speicherort dieser Daten aufzurufen.

Sie können auch löschen, indem Sie null als Wert für einen anderen Schreibvorgang wie set() oder update() angeben. Sie können diese Technik mit update() verwenden, um mehrere Kinder in einem einzigen API-Aufruf zu löschen.

Erhalten Sie ein Promise

Um zu wissen, wann Ihre Daten an den Firebase Realtime Database-Server übertragen werden, können Sie ein Promise verwenden. Sowohl set() als auch update() können ein Promise zurückgeben, mit dem Sie wissen können, wann der Schreibvorgang an die Datenbank übertragen wird.

Zuhörer trennen

Rückrufe werden entfernt, indem die Methode off() für Ihre Firebase-Datenbankreferenz aufgerufen wird.

Sie können einen einzelnen Listener entfernen, indem Sie ihn als Parameter an off() übergeben. Das Aufrufen von off() an der Position ohne Argumente entfernt alle Listener an dieser Position.

Das Aufrufen von off() auf einem übergeordneten Listener entfernt nicht automatisch Listener, die auf seinen untergeordneten Knoten registriert sind; off() muss auch für alle untergeordneten Listener aufgerufen werden, um den Rückruf zu entfernen.

Speichern Sie Daten als Transaktionen

Wenn Sie mit Daten arbeiten, die durch gleichzeitige Änderungen beschädigt werden könnten, wie z. B. inkrementelle Zähler, können Sie eine Transaktionsoperation verwenden. Sie können dieser Operation eine Aktualisierungsfunktion und einen optionalen Abschlussrückruf zuweisen. Die Update-Funktion nimmt den aktuellen Zustand der Daten als Argument und gibt den neuen gewünschten Zustand zurück, den Sie schreiben möchten. Wenn ein anderer Client an den Speicherort schreibt, bevor Ihr neuer Wert erfolgreich geschrieben wurde, wird Ihre Aktualisierungsfunktion erneut mit dem neuen aktuellen Wert aufgerufen, und der Schreibvorgang wird wiederholt.

In der Beispiel-Social-Blogging-App könnten Sie beispielsweise Benutzern erlauben, Beiträge zu markieren und die Markierung aufzuheben, und nachverfolgen, wie viele Sterne ein Beitrag erhalten hat, wie folgt:

Web version 9

import { getDatabase, ref, runTransaction } from "firebase/database";

function toggleStar(uid) {
  const db = getDatabase();
  const postRef = ref(db, '/posts/foo-bar-123');

  runTransaction(postRef, (post) => {
    if (post) {
      if (post.stars && post.stars[uid]) {
        post.starCount--;
        post.stars[uid] = null;
      } else {
        post.starCount++;
        if (!post.stars) {
          post.stars = {};
        }
        post.stars[uid] = true;
      }
    }
    return post;
  });
}

Web version 8

function toggleStar(postRef, uid) {
  postRef.transaction((post) => {
    if (post) {
      if (post.stars && post.stars[uid]) {
        post.starCount--;
        post.stars[uid] = null;
      } else {
        post.starCount++;
        if (!post.stars) {
          post.stars = {};
        }
        post.stars[uid] = true;
      }
    }
    return post;
  });
}

Durch die Verwendung einer Transaktion wird verhindert, dass die Anzahl der Sterne falsch ist, wenn mehrere Benutzer denselben Beitrag gleichzeitig markieren oder der Client veraltete Daten hatte. Wenn die Transaktion abgelehnt wird, gibt der Server den aktuellen Wert an den Client zurück, der die Transaktion mit dem aktualisierten Wert erneut ausführt. Dies wiederholt sich, bis die Transaktion akzeptiert wird oder Sie die Transaktion abbrechen.

Atomare serverseitige Inkremente

Im obigen Anwendungsfall schreiben wir zwei Werte in die Datenbank: die ID des Benutzers, der den Beitrag markiert/entmarkiert, und die erhöhte Anzahl der Sterne. Wenn wir bereits wissen, dass der Benutzer den Beitrag markiert, können wir anstelle einer Transaktion eine atomare Inkrementoperation verwenden.

Web version 9

function addStar(uid, key) {
  import { getDatabase, increment, ref, update } from "firebase/database";
  const dbRef = ref(getDatabase());

  const updates = {};
  updates[`posts/${key}/stars/${uid}`] = true;
  updates[`posts/${key}/starCount`] = increment(1);
  updates[`user-posts/${key}/stars/${uid}`] = true;
  updates[`user-posts/${key}/starCount`] = increment(1);
  update(dbRef, updates);
}

Web version 8

function addStar(uid, key) {
  const updates = {};
  updates[`posts/${key}/stars/${uid}`] = true;
  updates[`posts/${key}/starCount`] = firebase.database.ServerValue.increment(1);
  updates[`user-posts/${key}/stars/${uid}`] = true;
  updates[`user-posts/${key}/starCount`] = firebase.database.ServerValue.increment(1);
  firebase.database().ref().update(updates);
}

Dieser Code verwendet keine Transaktionsoperation, sodass er nicht automatisch erneut ausgeführt wird, wenn eine widersprüchliche Aktualisierung vorliegt. Da die Inkrementierungsoperation jedoch direkt auf dem Datenbankserver stattfindet, besteht keine Möglichkeit eines Konflikts.

Wenn Sie anwendungsspezifische Konflikte erkennen und zurückweisen möchten, z. B. wenn ein Benutzer einen Beitrag markiert, den er bereits zuvor markiert hat, sollten Sie benutzerdefinierte Sicherheitsregeln für diesen Anwendungsfall schreiben.

Offline mit Daten arbeiten

Wenn ein Client seine Netzwerkverbindung verliert, funktioniert Ihre App weiterhin ordnungsgemäß.

Jeder Client, der mit einer Firebase-Datenbank verbunden ist, verwaltet seine eigene interne Version aller aktiven Daten. Wenn Daten geschrieben werden, werden sie zuerst in diese lokale Version geschrieben. Der Firebase-Client synchronisiert diese Daten dann nach bestem Wissen und Gewissen mit den Remote-Datenbankservern und mit anderen Clients.

Daher lösen alle Schreibvorgänge in die Datenbank sofort lokale Ereignisse aus, bevor Daten auf den Server geschrieben werden. Das bedeutet, dass Ihre App unabhängig von Netzwerklatenz oder Konnektivität reaktionsfähig bleibt.

Sobald die Konnektivität wiederhergestellt ist, empfängt Ihre App die entsprechenden Ereignisse, sodass der Client mit dem aktuellen Serverstatus synchronisiert wird, ohne benutzerdefinierten Code schreiben zu müssen.

Wir werden mehr über das Offline-Verhalten in Erfahren Sie mehr über Online- und Offline-Funktionen sprechen..

Nächste Schritte