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 wprowadzić zmiany w Firebase Realtime Database za pomocą DataSnapshot lub 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 (patrz Co mogę zrobić za pomocą Cloud Functions?). (przykłady przypadków użycia znajdziesz poniżej).
  3. Otrzymuje obiekt danych, który zawiera zrzut danych przechowywanych w określonym dokumencie.

Aktywowanie funkcji Realtime Database

Twórz nowe funkcje dla zdarzeń Realtime Database za pomocą functions.database. Aby kontrolować, kiedy funkcja jest aktywowana, określ 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, aktualizacji lub usunięcia albo dowolnych zmian w ścieżce. Cloud Functions obsługuje te moduły obsługi zdarzeń w przypadku Realtime Database:

  • onWrite(), która jest aktywowana, gdy dane są tworzone, aktualizowane lub usuwane w Realtime Database.
  • onCreate(), który jest aktywowany, gdy w Realtime Database zostaną utworzone nowe dane.
  • onUpdate(), który jest aktywowany, gdy dane są aktualizowane w Realtime Database .
  • onDelete(), która jest aktywowana, 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 instancji Realtime Database projektu w Firebase. Na 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żki pasują do wszystkich zapisów, które dotyczą ścieżki, w tym zapisów, które występują w dowolnym miejscu poniżej. 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 o /foo/bar, a dane zdarzenia obejmują stare i nowe dane z /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ść, żądaj danych tylko na najgłębszym możliwym poziomie.

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

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

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

Obsługa danych zdarzeń

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ń onCreate i onDelete zwracany obiekt danych jest migawką 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żytkowników

Z obiektów EventContext.auth i EventContext.authType możesz uzyskać dostęp do informacji o użytkowniku, który wywołał funkcję, w tym do jego uprawnień. Może to być przydatne do egzekwowania reguł zabezpieczeń, 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 równoczesnością, 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));
      });
    });

Odczytaj poprzednią wartość

Obiekt Change ma właściwość before, która pozwala sprawdzić, co zostało zapisane w Realtime Database przed zdarzeniem. Właściwość before zwraca obiekt DataSnapshot, w którym wszystkie metody (np. val() i exists()) odwołują się do poprzedniej wartości. Nową wartość możesz odczytać ponownie, używając oryginalnego elementu DataSnapshot lub odczytując właściwość after. Ta właściwość w dowolnym obiekcie Change to kolejny obiekt DataSnapshot reprezentujący stan danych po wystąpieniu zdarzenia.

Na przykład właściwości before można użyć, aby funkcja zamieniała tekst na wielkie litery tylko podczas jego 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);
    });