Aktywatory bazy danych czasu rzeczywistego


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 oddzielnie. Możesz wprowadzić Firebase Realtime Database zmiany za pomocą DataSnapshot lub pakietu Admin SDK.

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

  1. Oczekuje na zmiany w określonej Realtime Database lokalizacji.
  2. Uruchamia się, gdy nastąpi zdarzenie, i wykonuje swoje zadania (patrz Co mogę zrobić za pomocą Cloud Functions?). przykłady przypadków użycia).
  3. Otrzymuje obiekt danych zawierający zrzut danych przechowywanych w określonym dokumencie.

Aktywowanie funkcji Realtime Database

Twórz nowe funkcje dla zdarzeń Realtime Database za pomocą functions.database. Aby określić, kiedy funkcja ma się uruchamiać, podaj jeden z modułów obsługi zdarzeń i ścieżkę Realtime Database, na 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, aktualizowania 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óre jest aktywowane, gdy dane są tworzone, aktualizowane lub usuwane w Realtime Database.
  • onCreate(), która jest aktywowana, gdy w Realtime Database utworzone zostaną nowe dane.
  • onUpdate(), która jest aktywowana, gdy dane w Realtime Database zostaną zaktualizowane .
  • onDelete(), która jest aktywowana, gdy dane są usuwane z Realtime Database .

Określ instancję i ścieżkę

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

  • Domyślna instancja Realtime Database: 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 operacji zapisu, które dotyczą ścieżki, w tym operacji zapisu, które mają miejsce w dowolnym miejscu poniżej niej. Jeśli ścieżka funkcji to /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 momencie /foo/bar, a dane zdarzenia obejmują stare i nowe dane w momencie /foo/bar. Jeśli dane zdarzenia mogą być duże, rozważ użycie wielu funkcji na głębszych ścieżkach zamiast jednej funkcji w pobliżu katalogu głównego bazy danych. Aby uzyskać najlepszą wydajność, wysyłaj żądania danych tylko na najniższym możliwym poziomie.

Składnik ścieżki możesz określić jako symbol wieloznaczny, umieszczając go w nawiasach klamrowych. Symbol ref('foo/{bar}') pasuje do dowolnego elementu podrzędnego elementu /foo. Wartości tych komponentó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"
  }
}

pasuje do ścieżki "/foo/{bar}" 2 razy: raz z "hello": "world" i raz 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 migawki reprezentujące stan danych przed zdarzeniem wywołującym i po nim. W przypadku zdarzeń onCreateonDelete zwracany obiekt danych jest zrzutem danych utworzonych lub usuniętych.

W tym przykładzie funkcja pobiera zrzut dla określonej ścieżki, przekształca 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żytkowników

Z poziomu EventContext.authEventContext.authType możesz uzyskać dostęp do informacji o użytkowniku, w tym do uprawnień użytkownika, który wywołał 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 niego i wykonywać operacje zapisu w jego imieniu. Aby uniknąć problemów z jednoczesnym dostępem, usuń instancję aplikacji w sposób pokazany 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()exists()) odnoszą się do poprzedniej wartości. Możesz ponownie odczytać nową wartość, używając pierwotnego parametru 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 zapewnienia, że funkcja będzie przekształcać tekst na wielkie litery tylko podczas jego 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);
    });