Wyzwalacze bazy danych czasu rzeczywistego


Dzięki Cloud Functions możesz obsługiwać zdarzenia w bazie danych Firebase Realtime Database bez konieczności aktualizowania kodu klienta. Cloud Functions umożliwia uruchamianie operacji w bazie danych czasu rzeczywistego z pełnymi uprawnieniami administracyjnymi i gwarantuje, że każda zmiana w bazie danych czasu rzeczywistego będzie przetwarzana indywidualnie. Zmiany w bazie danych Firebase Realtime Database możesz wprowadzać za pomocą DataSnapshot lub pakietu Admin SDK .

W typowym cyklu życia funkcja bazy danych Firebase Realtime Database wykonuje następujące czynności:

  1. Czeka na zmiany w określonej lokalizacji bazy danych czasu rzeczywistego.
  2. Uruchamia się, gdy wystąpi zdarzenie i wykonuje swoje zadania (zobacz Co mogę zrobić z Cloud Functions?, aby zapoznać się z przykładami przypadków użycia).
  3. Odbiera obiekt danych zawierający migawkę danych przechowywanych w określonym dokumencie.

Uruchom funkcję bazy danych czasu rzeczywistego

Utwórz nowe funkcje dla zdarzeń w bazie danych czasu rzeczywistego za pomocą functions.database . Aby kontrolować moment wyzwalania funkcji, określ jedną z procedur obsługi zdarzeń i podaj ścieżkę do bazy danych czasu rzeczywistego, w której będzie ona nasłuchiwać zdarzeń.

Ustaw procedurę obsługi zdarzeń

Funkcje umożliwiają obsługę zdarzeń z bazy danych czasu rzeczywistego na dwóch poziomach szczegółowości; możesz nasłuchiwać wyłącznie zdarzeń tworzenia, aktualizacji lub usuwania, możesz też nasłuchiwać wszelkich zmian w ścieżce. Cloud Functions obsługuje następujące procedury obsługi zdarzeń dla bazy danych czasu rzeczywistego:

  • onWrite() , która jest wyzwalana, gdy dane są tworzone, aktualizowane lub usuwane w bazie danych czasu rzeczywistego.
  • onCreate() , która jest uruchamiana po utworzeniu nowych danych w bazie danych czasu rzeczywistego.
  • onUpdate() , która jest wyzwalana, gdy dane są aktualizowane w bazie danych czasu rzeczywistego.
  • onDelete() , która jest wyzwalana w przypadku usunięcia danych z bazy danych czasu rzeczywistego.

Określ instancję i ścieżkę

Aby kontrolować, kiedy i gdzie funkcja powinna zostać uruchomiona, wywołaj ref(path) , aby określić ścieżkę i opcjonalnie określ instancję bazy danych czasu rzeczywistego za pomocą instance('INSTANCE_NAME') . Jeśli nie określisz instancji, funkcja zostanie wdrożona w domyślnej instancji Realtime Database dla projektu Firebase. Na przykład:

  • Domyślna instancja bazy danych czasu rzeczywistego: functions.database.ref('/foo/bar')
  • Instancja o nazwie „my-app-db-2”: functions.database.instance('my-app-db-2').ref('/foo/bar')

Metody te kierują funkcją do obsługi zapisów w określonej ścieżce w instancji bazy danych Realtime. Specyfikacje ścieżek odpowiadają wszystkim zapisom, które dotykają ścieżki, łącznie z zapisami, które mają miejsce gdziekolwiek poniżej niej. Jeśli ustawisz ścieżkę do swojej funkcji jako /foo/bar , dopasuje ona zdarzenia w obu tych lokalizacjach:

 /foo/bar
 /foo/bar/baz/really/deep/path

W obu przypadkach Firebase interpretuje, że zdarzenie ma miejsce w /foo/bar , a dane zdarzenia obejmują stare i nowe dane w /foo/bar . Jeśli dane zdarzeń mogą być duże, rozważ użycie wielu funkcji w głębszych ścieżkach zamiast pojedynczej funkcji w pobliżu katalogu głównego bazy danych. Aby uzyskać najlepszą wydajność, żądaj danych tylko na najgłębszym możliwym poziomie.

Możesz określić komponent ścieżki jako symbol wieloznaczny, otaczając go nawiasami klamrowymi; ref('foo/{bar}') dopasowuje dowolne dziecko /foo . Wartości tych komponentów ścieżki z symbolami wieloznacznymi są dostępne w obiekcie EventContext.params Twojej funkcji. W tym przykładzie wartość jest dostępna jako context.params.bar .

Ścieżki z symbolami wieloznacznymi mogą pasować do wielu zdarzeń w ramach jednego zapisu. Wkładka z

{
  "foo": {
    "hello": "world",
    "firebase": "functions"
  }
}

dopasowuje ścieżkę "/foo/{bar}" dwukrotnie: raz z "hello": "world" i ponownie z "firebase": "functions" .

Obsługuj dane zdarzeń

Podczas obsługi zdarzenia bazy danych czasu rzeczywistego zwracany obiekt danych to DataSnapshot . W przypadku zdarzeń onWrite lub onUpdate pierwszym parametrem jest obiekt Change zawierający dwie migawki reprezentujące stan danych przed i po zdarzeniu wyzwalającym. W przypadku zdarzeń onCreate i onDelete zwracany obiekt danych jest migawką utworzonych lub usuniętych danych.

W tym przykładzie funkcja pobiera migawkę dla określonej ścieżki, konwertuje ciąg znaków w tej lokalizacji na wielkie litery i zapisuje zmodyfikowany ciąg do bazy danych:

// Listens for new messages added to /messages/:pushId/original and creates an
// uppercase version of the message to /messages/:pushId/uppercase
exports.makeUppercase = functions.database.ref('/messages/{pushId}/original')
    .onCreate((snapshot, context) => {
      // Grab the current value of what was written to the Realtime Database.
      const original = snapshot.val();
      functions.logger.log('Uppercasing', context.params.pushId, original);
      const uppercase = original.toUpperCase();
      // You must return a Promise when performing asynchronous tasks inside a Functions such as
      // writing to the Firebase Realtime Database.
      // Setting an "uppercase" sibling in the Realtime Database returns a Promise.
      return snapshot.ref.parent.child('uppercase').set(uppercase);
    });

Dostęp do informacji uwierzytelniających użytkownika

Z EventContext.auth i EventContext.authType można uzyskać dostęp do informacji o użytkowniku, w tym o uprawnieniach, dla użytkownika, który uruchomił funkcję. Może to być przydatne do egzekwowania reguł bezpieczeństwa, umożliwiając Twojej funkcji wykonywanie różnych operacji w zależności od poziomu uprawnień użytkownika:

const functions = require('firebase-functions');
const admin = require('firebase-admin');

exports.simpleDbFunction = functions.database.ref('/path')
    .onCreate((snap, context) => {
      if (context.authType === 'ADMIN') {
        // do something
      } else if (context.authType === 'USER') {
        console.log(snap.val(), 'written by', context.auth.uid);
      }
    });

Można także wykorzystać informacje uwierzytelniające użytkownika do „podszywania się” pod użytkownika i wykonywania operacji zapisu w jego imieniu. Pamiętaj, aby usunąć instancję aplikacji, jak pokazano poniżej, aby zapobiec problemom ze współbieżnością:

exports.impersonateMakeUpperCase = functions.database.ref('/messages/{pushId}/original')
    .onCreate((snap, context) => {
      const appOptions = JSON.parse(process.env.FIREBASE_CONFIG);
      appOptions.databaseAuthVariableOverride = context.auth;
      const app = admin.initializeApp(appOptions, 'app');
      const uppercase = snap.val().toUpperCase();
      const ref = snap.ref.parent.child('uppercase');

      const deleteApp = () => app.delete().catch(() => null);

      return app.database().ref(ref).set(uppercase).then(res => {
        // Deleting the app is necessary for preventing concurrency leaks
        return deleteApp().then(() => res);
      }).catch(err => {
        return deleteApp().then(() => Promise.reject(err));
      });
    });

Odczytywanie poprzedniej wartości

Obiekt Change ma właściwość before , która pozwala sprawdzić, co zostało zapisane w bazie danych czasu rzeczywistego przed zdarzeniem. Właściwość before zwraca DataSnapshot , w której wszystkie metody (na przykład val() i exists() ) odwołują się do poprzedniej wartości. Możesz ponownie odczytać nową wartość, używając oryginalnej DataSnapshot lub czytając właściwość after . Ta właściwość w przypadku dowolnej Change jest kolejnym DataSnapshot reprezentującym stan danych po wystąpieniu zdarzenia.

Na przykład można użyć właściwości before , aby upewnić się, że przy pierwszym utworzeniu funkcja będzie pisać tylko wielkimi literami:

exports.makeUppercase = functions.database.ref('/messages/{pushId}/original')
    .onWrite((change, context) => {
      // Only edit data when it is first created.
      if (change.before.exists()) {
        return null;
      }
      // Exit when the data is deleted.
      if (!change.after.exists()) {
        return null;
      }
      // Grab the current value of what was written to the Realtime Database.
      const original = change.after.val();
      console.log('Uppercasing', context.params.pushId, original);
      const uppercase = original.toUpperCase();
      // You must return a Promise when performing asynchronous tasks inside a Functions such as
      // writing to the Firebase Realtime Database.
      // Setting an "uppercase" sibling in the Realtime Database returns a Promise.
      return change.after.ref.parent.child('uppercase').set(uppercase);
    });