Aktywatory Bazy danych czasu rzeczywistego (1 generacji)

Dzięki Cloud Functions możesz obsługiwać zdarzenia w Firebase Realtime Database bez konieczności aktualizowania kodu klienta. Cloud Functions umożliwia wykonywanie operacji Realtime Database z pełnymi uprawnieniami administracyjnymi i zapewnia, że każda zmiana w Realtime Database jest przetwarzana indywidualnie. Możesz wprowadzać Firebase Realtime Database zmiany za pomocą DataSnapshot lub za pomocą pakietu Admin SDK.

W typowym cyklu życia funkcja Firebase Realtime Database wykonuje te czynności:

  1. Czeka na zmiany w określonej lokalizacji Realtime Database.
  2. Uruchamia się, gdy wystąpi zdarzenie, i wykonuje swoje zadania (przykłady zastosowań znajdziesz w artykule Co można zrobić za pomocą Cloud Functions?
  3. Otrzymuje obiekt danych zawierający zrzut danych przechowywanych w określonym dokumencie.

Uruchamianie funkcjiRealtime Database

Twórz nowe funkcje dla Realtime Database zdarzeń za pomocą functions.database. Aby kontrolować, kiedy funkcja ma się uruchamiać, określ jeden z modułów obsługi zdarzeń i określ ścieżkę Realtime Database, w której będzie nasłuchiwać zdarzeń.

Ustawianie modułu obsługi zdarzeń

Funkcje umożliwiają obsługę zdarzeń Realtime Database na 2 poziomach szczegółowości. Możesz nasłuchiwać tylko zdarzeń tworzenia, aktualizacji lub usuwania albo nasłuchiwać dowolnych zmian w ścieżce. Cloud Functions obsługuje te moduły obsługi zdarzeń w przypadku Realtime Database:

  • onWrite(), który uruchamia się, gdy dane są tworzone, aktualizowane lub usuwane w Realtime Database.
  • onCreate(), który uruchamia się, gdy w Realtime Database tworzone są nowe dane.
  • onUpdate(), który uruchamia się, gdy dane są aktualizowane w Realtime Database .
  • onDelete(), który uruchamia się, gdy dane są usuwane z Realtime Database .

Określanie instancji i ścieżki

Aby kontrolować, kiedy i gdzie funkcja ma się uruchamiać, wywołaj ref(path) aby określić ścieżkę, i opcjonalnie określ instancję Realtime Database za pomocą instance('INSTANCE_NAME'). Jeśli nie określisz instancji, funkcja zostanie wdrożona w domyślnej Realtime Database instancji w projekcie w Firebase. Na przykład:

  • Domyślna Realtime Database instancja: 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 Realtime Database. Specyfikacje ścieżek pasują do wszystkich zapisów, które dotyczą ścieżki, w tym zapisów, które występują w dowolnym miejscu poniżej niej. Jeśli ustawisz ścieżkę funkcji jako /foo/bar, będzie ona pasować do zdarzeń w obu tych lokalizacjach:

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

W obu przypadkach Firebase interpretuje, że zdarzenie występuje w /foo/bar, a dane zdarzenia zawierają stare i nowe dane w /foo/bar. Jeśli dane zdarzenia 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.

Możesz określić komponent ścieżki jako symbol wieloznaczny, otaczając go nawiasami klamrowymi . ref('foo/{bar}') pasuje do każdego elementu podrzędnego /foo. Wartości tych komponentów ścieżki z symbolami wieloznacznymi są dostępne w EventContext.params obiekcie 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. Wstawienie

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

pasuje do ścieżki "/foo/{bar}" 2 razy: raz z "hello": "world" i ponownie z "firebase": "functions".

Obsługa danych zdarzenia

Podczas obsługi zdarzenia Realtime Database zwracany obiekt danych to DataSnapshot. W przypadku zdarzeń onWrite lub onUpdate pierwszym parametrem jest obiekt Change, który zawiera 2 zrzuty reprezentujące stan danych przed i po zdarzeniu wyzwalającym. W przypadku zdarzeń onCreate i onDelete zwracany obiekt danych jest zrzutem utworzonych lub usuniętych danych.

W tym przykładzie funkcja pobiera zrzut dla określonej ścieżki, konwertuje ciąg znaków w tej lokalizacji na wielkie litery i zapisuje zmodyfikowany ciąg znaków 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 o uwierzytelnianiu użytkownika

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

const functions = require('firebase-functions/v1');
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żesz też wykorzystać informacje o uwierzytelnianiu użytkownika, aby „podszyć się” pod użytkownika i wykonywać operacje zapisu w jego imieniu. Aby uniknąć problemów z współbieżnością, usuń instancję aplikacji w sposób opisany poniżej:

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 umożliwia sprawdzenie, co zostało zapisane w Realtime Database przed zdarzeniem. Właściwość before zwraca DataSnapshot, w którym wszystkie metody (np. val() i exists()) odnoszą się do poprzedniej wartości. Możesz ponownie odczytać nową wartość, używając oryginalnego DataSnapshot lub odczytując właściwość after. Ta właściwość w dowolnym Change to kolejny DataSnapshot reprezentujący stan danych po wystąpieniu zdarzenia.

Na przykład właściwość before może służyć do sprawdzania, czy funkcja zmienia tekst na wielkie litery tylko wtedy, gdy jest on tworzony po raz pierwszy:

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