Realtime Database tetikleyicileri

Cloud Functions ile istemci kodunu güncellemeye gerek kalmadan Firebase Realtime Database içindeki etkinlikleri yönetebilirsiniz. Cloud Functions, tam yönetici ayrıcalıklarıyla Realtime Database işlemleri yapmanıza olanak tanır ve Realtime Database üzerinde yapılan her değişikliğin ayrı ayrı işlenmesini sağlar. Veri anlık görüntüsü veya Admin SDK aracılığıyla Firebase Realtime Database değişiklik yapabilirsiniz.

Tipik bir yaşam döngüsünde Firebase Realtime Database işlevi şunları yapar:

  1. Belirli bir Realtime Database yolunda değişiklik yapılmasını bekler.
  2. Bir etkinlik gerçekleştiğinde tetiklenir ve görevlerini yerine getirir.
  3. Bu yolda depolanan verilerin anlık görüntüsünü içeren bir veri nesnesi alır.

Firebase Realtime Database uygulamasında veritabanı düğümlerinin yazılması, oluşturulması, güncellenmesi veya silinmesine yanıt olarak bir işlevi tetikleyebilirsiniz. İşlevin ne zaman tetikleneceğini kontrol etmek için etkinlik işleyicilerden birini ve etkinlikleri dinleyeceği Realtime Database yolunu belirtin.

İşlev konumunu ayarlama

Realtime Database örneğinin konumu ile işlevin konumu arasındaki mesafe, önemli bir ağ gecikmesine neden olabilir. Ayrıca bölgeler arasında uyuşmazlık olması dağıtımın başarısız olmasına yol açabilir. Bu durumları önlemek için işlev konumunu, veritabanı örneği konumuyla eşleşecek şekilde belirtin.

Realtime Database etkinliklerini yönetme

Functions, Realtime Database etkinliklerini iki farklı düzeyde işlemenize olanak tanır. Yalnızca yazma, oluşturma, güncelleme veya silme etkinliklerini dinleyebilir ya da bir referansta yapılan her türlü değişikliği dinleyebilirsiniz.

Realtime Database etkinliklerine yanıt vermek için şu işleyiciler kullanılabilir:

Node.js

  • onValueWritten() Realtime Database uygulamasında veri oluşturulduğunda, güncellendiğinde veya silindiğinde tetiklenir.
  • onValueCreated() Yalnızca Realtime Database içinde veri oluşturulduğunda tetiklenir.
  • onValueUpdated() Yalnızca Realtime Database içinde veri güncellendiğinde tetiklenir.
  • onValueDeleted() Yalnızca Realtime Database içinde veri silindiğinde tetiklenir.

Python

  • on_value_written() Realtime Database uygulamasında veri oluşturulduğunda, güncellendiğinde veya silindiğinde tetiklenir.
  • on_value_created() Yalnızca Realtime Database içinde veri oluşturulduğunda tetiklenir.
  • on_value_updated() Yalnızca Realtime Database içinde veri güncellendiğinde tetiklenir.
  • on_value_deleted() Yalnızca Realtime Database içinde veri silindiğinde tetiklenir.

Gerekli modülleri içe aktarma

İşlev kaynağınızda, kullanmak istediğiniz SDK modüllerini içe aktarmanız gerekir. Bu örnekte, Realtime Database modülüne yazmak için Firebase Admin SDK modülüyle birlikte HTTP ve Realtime Database modüllerini içe aktarmak gerekir.

Node.js

// The Cloud Functions for Firebase SDK to setup triggers and logging.
const {onRequest} = require("firebase-functions/https");
const {onValueCreated} = require("firebase-functions/database");
const {logger} = require("firebase-functions");

// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require("firebase-admin");
admin.initializeApp();

Python

# The Cloud Functions for Firebase SDK to create Cloud Functions and set up triggers.
from firebase_functions import db_fn, https_fn

# The Firebase Admin SDK to access the Firebase Realtime Database.
from firebase_admin import initialize_app, db

app = initialize_app()

Örnek ve yol belirtin

İşlevinizin ne zaman ve nerede tetikleneceğini kontrol etmek için işlevinizi bir yol ve isteğe bağlı olarak bir Realtime Database örneğiyle yapılandırın. Bir örnek belirtmezseniz işlev, işlev bölgesindeki tüm Realtime Database örneklerini dinler. Aynı bölgedeki belirli bir örnek alt kümesine dağıtmak için bir Realtime Database örnek kalıbı da belirtebilirsiniz.

Örneğin:

Node.js

// All Realtime Database instances at path "/user/{uid}"
// There must be at least one Realtime Database present.
const onWrittenFunctionDefault = onValueWritten("/user/{uid}", (event) => {
  // …
});

// Instance named "my-app-db-2", at path "/user/{uid}".
// The "my-app-db-2" instance must exist in this region.
const OnWrittenFunctionInstance = onValueWritten(
  {
    ref: "/user/{uid}",
    instance: "my-app-db-2"
  },
  (event) => {
    // …
  }
);

// Instance with "my-app-db-" prefix, at path "/user/{uid}", where uid ends with @gmail.com.
// There must be at least one Realtime Database with "my-app-db-*" prefix in this region.
const onWrittenFunctionInstance = onValueWritten(
  {
    ref: "/user/{uid=*@gmail.com}",
    instance: "my-app-db-*"
  },
  (event) => {
    // …
  }
);

Python

# All Realtime Database instances at path "/user/{uid}"
# There must be at least one Realtime Database present.
@db_fn.on_value_written(r"/user/{uid}")
def onwrittenfunctiondefault(event: db_fn.Event[db_fn.Change]):
    # ...
    pass

# Instance named "my-app-db-2", at path "/user/{uid}".
# The "my-app-db-2" instance must exist in this region.
@db_fn.on_value_written(
    reference=r"/user/{uid}",
    instance="my-app-db-2",
)
def on_written_function_instance(event: db_fn.Event[db_fn.Change]):
    # ...
    pass

# Instance with "my-app-db-" prefix, at path "/user/{uid}", where uid ends with @gmail.com.
# There must be at least one Realtime Database with "my-app-db-*" prefix in this region.
@db_fn.on_value_written(
    reference=r"/user/{uid=*@gmail.com}",
    instance="my-app-db-*",
)
def on_written_function_instance(event: db_fn.Event[db_fn.Change]):
    # ...
    pass

Bu parametreler, işlevinizi Realtime Database örneğindeki belirli bir yolda yazma işlemlerini gerçekleştirmeye yönlendirir.

Yol spesifikasyonları, bir yola dokunan tüm yazma işlemleriyle (yolun altındaki herhangi bir yerde gerçekleşen yazma işlemleri dahil) eşleşir. İşlevinizin yolunu /foo/bar olarak ayarlarsanız bu iki konumdaki etkinliklerle eşleşir:

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

Her iki durumda da Firebase, etkinliğin /foo/bar tarihinde gerçekleştiğini yorumlar ve etkinlik verileri, /foo/bar tarihindeki eski ve yeni verileri içerir. Etkinlik verileri büyük olabilecekse veritabanınızın köküne yakın tek bir işlev yerine daha derin yollarda birden fazla işlev kullanmayı deneyin. En iyi performans için yalnızca mümkün olan en derin düzeyde veri isteyin.

Joker karakter ve yakalama

Çekim için {key}, {key=*}, {key=prefix*}, {key=*suffix} kullanabilirsiniz. *, prefix*, *suffix tek segmentli joker karakter kullanımı için. Not: **, Realtime Database tarafından desteklenmeyen çok segmentli joker karakterleri temsil eder. Yol kalıplarını anlama başlıklı makaleyi inceleyin.

Yol joker karakteri kullanma Bir yol bileşenini joker karakter olarak belirtebilirsiniz:

  • Yıldız işareti (*) kullanma Örneğin, foo/*, foo/ altındaki düğüm hiyerarşisinin bir düzey altındaki tüm alt öğelerle eşleşir.
  • Yalnızca yıldız işareti (*) içeren bir segment kullanma. Örneğin, foo/app*-us, foo/ altındaki app ön eki ve -us son eki olan tüm alt segmentlerle eşleşir.

Joker karakter içeren yollar, örneğin tek bir yazma işleminden birden fazla etkinlikle eşleşebilir. An insert of

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

"/foo/*" yoluyla iki kez eşleşir: bir kez "hello": "world" ile ve bir kez daha "firebase": "functions" ile.

Yol yakalama. Yol eşleşmelerini, işlev kodunuzda kullanılacak adlandırılmış değişkenlere kaydedebilirsiniz (ör. /user/{uid}, /user/{uid=*-us}).

Yakalama değişkenlerinin değerleri, işlevinizin database.DatabaseEvent.params nesnesinde bulunur.

Örnek joker karakter kullanımı. Joker karakter kullanarak bir örnek bileşeni de belirtebilirsiniz. Örnek joker karakterinde önek, sonek veya her ikisi de bulunabilir (ör. my-app-*-prod).

Joker karakter ve yakalama referansı

Cloud Functions (2. nesil) ve Realtime Database ile ref ve instance belirtilirken bir kalıp kullanılabilir. Her tetikleyici arayüzünde, bir işlevin kapsamını belirlemek için aşağıdaki seçenekler bulunur:

ref belirtme instance belirtme Davranış
Tek (/foo/bar) Belirtilmedi İşleyiciyi, işlev bölgesindeki tüm örneklere uygular.
Tek (/foo/bar) Tek (‘my-new-db') İşleyiciyi, işlev bölgesindeki belirli bir örnekle sınırlandırır.
Tekli (/foo/bar) Desen (‘inst-prefix*') İşleyiciyi, işlev bölgesindeki kalıpla eşleşen tüm örneklere uygular.
Desen (/foo/{bar}) Belirtilmedi İşleyiciyi, işlev bölgesindeki tüm örneklere uygular.
Desen (/foo/{bar}) Tek (‘my-new-db') İşleyiciyi, işlev bölgesindeki belirli bir örnekle sınırlandırır.
Desen (/foo/{bar}) Desen (‘inst-prefix*') İşleyiciyi, işlev bölgesindeki kalıpla eşleşen tüm örneklere uygular.

Etkinlik verilerini işleme

Bir Realtime Database etkinliği tetiklendiğinde işleyici işlevinize bir Event nesnesi iletir. Bu nesnenin, oluşturma ve silme etkinlikleri için oluşturulan veya silinen verilerin anlık görüntüsünü içeren bir data özelliği vardır.

Bu örnekte işlev, referans verilen yolun verilerini alır, bu konumdaki dizeyi büyük harfe dönüştürür ve değiştirilen dizeyi veritabanına yazar:

Node.js

// Listens for new messages added to /messages/:pushId/original and creates an
// uppercase version of the message to /messages/:pushId/uppercase
// for all databases in 'us-central1'
exports.makeuppercase = onValueCreated(
    "/messages/{pushId}/original",
    (event) => {
    // Grab the current value of what was written to the Realtime Database.
      const original = event.data.val();
      logger.log("Uppercasing", event.params.pushId, original);
      const uppercase = original.toUpperCase();
      // You must return a Promise when performing
      // asynchronous tasks inside a function, such as
      // writing to the Firebase Realtime Database.
      // Setting an "uppercase" sibling in the
      // Realtime Database returns a Promise.
      return event.data.ref.parent.child("uppercase").set(uppercase);
    },
);

Python

@db_fn.on_value_created(reference="/messages/{pushId}/original")
def makeuppercase(event: db_fn.Event[Any]) -> None:
    """Listens for new messages added to /messages/{pushId}/original and
    creates an uppercase version of the message to /messages/{pushId}/uppercase
    """

    # Grab the value that was written to the Realtime Database.
    original = event.data
    if not isinstance(original, str):
        print(f"Not a string: {event.reference}")
        return

    # Use the Admin SDK to set an "uppercase" sibling.
    print(f"Uppercasing {event.params['pushId']}: {original}")
    upper = original.upper()
    parent = db.reference(event.reference).parent
    if parent is None:
        print("Message can't be root node.")
        return
    parent.child("uppercase").set(upper)

Önceki değeri okuma

write veya update etkinlikleri için data özelliği, tetikleyici etkinlikten önceki ve sonraki veri durumunu temsil eden iki anlık görüntü içeren bir Change nesnesidir. Change nesnesinin, etkinlikten önce Realtime Database konumuna kaydedilenleri incelemenize olanak tanıyan bir before özelliği ve etkinlik gerçekleştikten sonra verilerin durumunu temsil eden bir after özelliği vardır.

Örneğin, before özelliği, işlevin yalnızca ilk oluşturulduğunda metni büyük harfe dönüştürmesini sağlamak için kullanılabilir:

Node.js

  exports makeUppercase = onValueWritten("/messages/{pushId}/original", (event) => {
        // Only edit data when it is first created.
        if (event.data.before.exists()) {
          return null;
        }
        // Exit when the data is deleted.
        if (!event.data.after.exists()) {
          return null;
        }
        // Grab the current value of what was written to the Realtime Database.
        const original = event.data.after.val();
        console.log('Uppercasing', event.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 event.data.after.ref.parent.child('uppercase').set(uppercase);
      });

Python

@db_fn.on_value_written(reference="/messages/{pushId}/original")
def makeuppercase2(event: db_fn.Event[db_fn.Change]) -> None:
    """Listens for new messages added to /messages/{pushId}/original and
    creates an uppercase version of the message to /messages/{pushId}/uppercase
    """

    # Only edit data when it is first created.
    if event.data.before is not None:
        return

    # Exit when the data is deleted.
    if event.data.after is None:
        return

    # Grab the value that was written to the Realtime Database.
    original = event.data.after
    if not hasattr(original, "upper"):
        print(f"Not a string: {event.reference}")
        return

    # Use the Admin SDK to set an "uppercase" sibling.
    print(f"Uppercasing {event.params['pushId']}: {original}")
    upper = original.upper()
    parent = db.reference(event.reference).parent
    if parent is None:
        print("Message can't be root node.")
        return
    parent.child("uppercase").set(upper)

Kimlik doğrulama bağlamına erişme

RTDB Eventarc etkinlikleri tarafından tetiklenen işlevlerde kimlik doğrulama bağlamı, etkinlik yüküne dahil edilir:

  • authtype: Etkinliği tetikleyen asılın türü. Olası değerler şunlardır:
    • app_user: Geliştiricinin uygulamasının son kullanıcısı.
    • admin: Bir hizmet hesabı.
    • unauthenticated: Kimliği doğrulanmamış bir kullanıcı.
    • unknown: Kimlik doğrulama bilgileri mevcut olmadığında varsayılan olarak kullanılır.
  • authid: Yetki verilen kişinin benzersiz tanımlayıcısı.
    • authtype, app_user ise bu, kullanıcının UID'sidir.
    • authtype, admin ise bu, hizmet hesabı veya IAM kullanıcı e-postasıdır.

Bu kod, yalnızca işlevi tetikleyen kullanıcı yönetici değilse mesajın metnini büyük harfe dönüştürür. Ayrıca kod, mesajı tetikleyen kullanıcının mesajın gerçek göndereni olup olmadığını da kontrol eder.

Node.js

// The Cloud Functions for Firebase SDK to setup triggers and logging.
const {onValueWritten} = require("firebase-functions/v2/database");
const {logger} = require("firebase-functions");
const admin = require("firebase-admin");

admin.initializeApp();

exports.dbtrigger = onValueWritten("/messages/{pushId}/original", async (event) => {
  // 1. Check whether authtype is admin. If it is, skip this operation.
  if (event.authType === "admin") {
    logger.log("Modification by admin detected. Skipping uppercase conversion.");
    return null;
  }

  // 2. Retrieve the userID of the sender (assumed sibling node 'senderId')
  const snapshot = await event.data.after.ref.parent.child("senderId").get();
  const senderId = snapshot.val();

  // 3. Check if userID of sender of message = event.authid
  if (senderId !== event.authId) {
    logger.error(`Unauthorized write: senderId (${senderId}) does not match authId (${event.authId})`);
    return null;
  }

  // Grab the value that was written to the Realtime Database.
  const original = event.data.after.val();
  logger.log("Uppercasing", event.params.pushId, original);
  const uppercase = original.toUpperCase();

  // Return the promise to set the "uppercase" sibling node.
  return event.data.after.ref.parent.child("uppercase").set(uppercase);
});

Python

from firebase_functions import db_fn
from firebase_admin import initialize_app, db

initialize_app()

@db_fn.on_value_written(reference="/messages/{pushId}/original")
def makeuppercase(event: db_fn.Event[db_fn.Change]) -> None:
    # 1. Check whether authtype is admin. If it is, skip this operation.
    if event.auth_type == "admin":
        print("Admin user detected. Skipping.")
        return

    # 2. Retrieve the userID of the sender (assumed sibling node: 'senderId')
    parent_ref = db.reference(event.reference).parent
    sender_id = parent_ref.child("senderId").get()

    # 3. Check if userID of sender = event.auth_id
    if sender_id != event.auth_id:
        print(f"Unauthorized: sender_id {sender_id} != auth_id {event.auth_id}")
        return

    # Exit when the data is deleted.
    if event.data.after is None:
        return

    # Grab the value and uppercase it
    original = event.data.after
    if not isinstance(original, str):
        return

    print(f"Uppercasing {event.params['pushId']}: {original}")
    upper = original.upper()
    parent_ref.child("uppercase").set(upper)