Wyzwalacze bazy danych czasu rzeczywistego

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

W typowym cyklu życia funkcja bazy danych czasu rzeczywistego Firebase wykonuje te czynności:

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

Uruchom funkcję bazy danych czasu rzeczywistego

Utwórz nowe funkcje dla zdarzeń Realtime Database za pomocą functions.database . Aby kontrolować, kiedy funkcja jest wyzwalana, określ jeden z programów obsługi zdarzeń i określ ścieżkę bazy danych czasu rzeczywistego, w której będzie nasłuchiwać zdarzeń.

Ustaw obsługę zdarzeń

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

  • onWrite() , który jest wyzwalany, gdy dane są tworzone, aktualizowane lub usuwane w Bazie danych czasu rzeczywistego.
  • onCreate() , który jest wyzwalany, gdy w Bazie danych czasu rzeczywistego tworzone są nowe dane.
  • onUpdate() , który jest wyzwalany, gdy dane są aktualizowane w Bazie Danych Czasu Rzeczywistego .
  • onDelete() , który jest wyzwalany, gdy dane są usuwane z Bazy danych czasu rzeczywistego .

Określ instancję i ścieżkę

Aby kontrolować, kiedy i gdzie funkcja powinna być wyzwalana, 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 Bazy danych czasu rzeczywistego 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')

Te metody kierują funkcję do obsługi zapisów w określonej ścieżce w instancji bazy danych czasu rzeczywistego. Specyfikacje ścieżki pasują do wszystkich zapisów, które dotykają ścieżki, w tym zapisów, które mają miejsce gdziekolwiek poniżej. Jeśli ustawisz ścieżkę dla 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 zawierają 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 jednej funkcji w pobliżu katalogu głównego bazy danych. Aby uzyskać najlepszą wydajność, żądaj danych tylko na najgłębszym możliwym poziomie.

Składnik ścieżki można określić jako symbol wieloznaczny, otaczając go nawiasami klamrowymi; ref('foo/{bar}') pasuje do dowolnego elementu podrzędnego /foo . Wartości tych składników ścieżki z symbolami wieloznacznymi są dostępne w obiekcie EventContext.params funkcji. W tym przykładzie wartość jest dostępna jako context.params.bar .

Ścieżki z symbolami wieloznacznymi mogą pasować do wielu zdarzeń z jednego zapisu. Wstawka

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

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

Obsługa danych zdarzeń

Podczas obsługi zdarzenia Realtime Database zwróconym obiektem danych jest 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ą danych utworzonych lub usuniętych.

W tym przykładzie funkcja pobiera migawkę dla określonej ścieżki jako snap , konwertuje ciąg w tej lokalizacji na wielkie litery i zapisuje ten zmodyfikowany ciąg w bazie 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);
    });

Uzyskiwanie dostępu do informacji uwierzytelniających użytkownika

Z EventContext.auth i EventContext.authType można uzyskać dostęp do informacji o użytkowniku, w tym uprawnień, dla użytkownika, który wyzwolił 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);
      }
    });

Ponadto informacje dotyczące uwierzytelniania użytkownika można wykorzystać 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órym wszystkie metody (na przykład val() i exists() ) odwołują się do poprzedniej wartości. Nową wartość można odczytać ponownie, używając oryginalnego DataSnapshot lub odczytując właściwość after . Ta właściwość każdej Change jest kolejnym DataSnapshot reprezentującym stan danych po wystąpieniu zdarzenia.

Na przykład właściwość before może być użyta, aby upewnić się, że funkcja używa tylko wielkich liter podczas pierwszego tworzenia:

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);
    });