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 osobno. Zmiany w Firebase Realtime Database możesz wprowadzać za pomocą interfejsu DataSnapshot lub pakietu Admin SDK.

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

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

Aktywowanie funkcji Realtime Database

Utwórz nowe funkcje dla Realtime Database zdarzeń dzięki functions.database. Aby określić, kiedy ma się aktywować funkcja, podaj jeden z modułów obsługi zdarzeń i ścieżkę Realtime Database, na której będzie ona nasłuchiwać zdarzeń.

Konfigurowanie modułu obsługi zdarzenia

Funkcje umożliwiają obsługę zdarzeń Realtime Database na 2 poziomach dokładności: możesz nasłuchiwać wyłącznie tworzenia, aktualizowania i zdarzeń usuwania. Możesz też nasłuchiwać dowolnej zmiany ścieżki. Cloud Functions obsługuje te moduły obsługi zdarzeń: Realtime Database:

  • onWrite(), które jest wywoływane, gdy w tabeli Realtime Database tworzone, aktualizowane lub usuwane są dane.
  • onCreate(), która jest uruchaniana, gdy w tabeli Realtime Database są tworzone nowe dane.
  • onUpdate(), które jest wywoływane, gdy dane są aktualizowane w tabeli Realtime Database.
  • onDelete(), które uruchamia się, gdy dane z Realtime Database zostają usunięte .

Podaj instancję i ścieżkę

Aby kontrolować, kiedy i gdzie ma być uruchamiana Twoja funkcja, wywołaj ref(path) , by podać ścieżkę i opcjonalnie wskazać instancję Realtime Database dzięki instance('INSTANCE_NAME'). Jeśli nie chcesz i wskaż instancję, funkcja zostanie wdrożona w domyślnej instancji Realtime Database dla projekt 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 zapisu w określonym miejscu w obiekcie Realtime Database. specyfikacje ścieżki pasują do wszystkich zapisów, które dotykają ścieżki, w tym zapisy które dzieje się w dowolnym miejscu pod nią. Jeśli ustawisz ścieżkę dla Twojej funkcji jako /foo/bar, pasuje do zdarzeń w obu tych lokalizacjach:

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

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

Możesz określić element ścieżki jako symbol wieloznaczny, otaczając go nawiasami klamrowymi. ref('foo/{bar}') pasuje do dowolnego elementu podrzędnego elementu /foo. Wartości tych komponentów ścieżki symbolu wieloznacznego 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 ponownie z "firebase": "functions".

Obsługa danych zdarzenia

Podczas obsługi zdarzenia Realtime Database zwracany obiekt danych to DataSnapshot W przypadku zdarzeń onWrite i onUpdate wartość Pierwszy parametr to obiekt Change, który zawiera 2 zrzuty reprezentujących stan danych przed oraz po zdarzeniu aktywującym. W przypadku zdarzeń onCreate i onDelete zwracany obiekt danych to zrzut danych utworzonych lub usuniętych.

W tym przykładzie funkcja pobiera zrzut dysku dla: w podanej ścieżce, zamienia ciąg znaków w tej lokalizacji na wielkie litery, i zapisuje 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);
    });

Dostęp do informacji o uwierzytelnianiu użytkowników

Z poziomu EventContext.authEventContext.authType możesz uzyskać informacje o użytkowniku, w tym jego uprawnienia, które wywołały funkcję. Może to być przydatne do egzekwowania zasad bezpieczeństwa, ponieważ pozwala funkcji wykonywać różne operacje 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 uwierzytelniające użytkownika, aby „udawać” tego użytkownika i wykonywać operacje zapisu w jego imieniu. Aby uniknąć problemów z jednoczestronnością, usuń instancję aplikacji w ten sposób:

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

Odczyt poprzedniej wartości

Obiekt Change ma właściwość before, która umożliwia sprawdzenie, co zostało zapisane w obiekcie Realtime Database przed zdarzeniem. Właściwość before zwraca wartość DataSnapshot, gdzie wszystkie (na przykład val() i exists()) odwołują się do poprzedniej wartości. Możesz ponownie odczytać nową wartość, używając zarówno argumentu oryginalny DataSnapshot lub czytając after. usłudze. Ta usługa w dowolnym miejscu Change to inna usługa DataSnapshot reprezentująca wartość stan danych po wystąpieniu zdarzenia.

Na przykład za pomocą właściwości before możesz się upewnić, że funkcja będzie zamieniać tekst na wielkie litery tylko przy pierwszym utworzeniu:

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