Cloud Functions की मदद से रीयल टाइम डेटाबेस को बढ़ाना

Cloud Functions की मदद से, Firebase Realtime Database में होने वाले इवेंट को मैनेज किया जा सकता है. इसके लिए, क्लाइंट कोड को अपडेट करने की ज़रूरत नहीं होती. Cloud Functions की मदद से, Realtime Database पर एडमिन के सभी अधिकारों के साथ कार्रवाइयां की जा सकती हैं. साथ ही, यह पक्का किया जाता है कि Realtime Database में होने वाले हर बदलाव को अलग-अलग प्रोसेस किया जाए. डेटा स्नैपशॉट या Admin SDK की मदद से, Firebase Realtime Database बदलाव किए जा सकते हैं.

आम तौर पर, Firebase Realtime Database फ़ंक्शन ये काम करता है:

  1. किसी खास Realtime Database पाथ में होने वाले बदलावों का इंतज़ार करता है.
  2. कोई इवेंट होने पर ट्रिगर होता है और अपने टास्क पूरे करता है.
  3. इसे एक डेटा ऑब्जेक्ट मिलता है, जिसमें उस पाथ पर सेव किए गए डेटा का स्नैपशॉट होता है.

Firebase Realtime Database में डेटाबेस नोड को लिखने, बनाने, अपडेट करने या मिटाने पर, किसी फ़ंक्शन को ट्रिगर किया जा सकता है.Firebase Realtime Database यह कंट्रोल करने के लिए कि फ़ंक्शन कब ट्रिगर हो, इवेंट हैंडलर में से कोई एक चुनें. साथ ही, Realtime Database का वह पाथ चुनें जहां यह इवेंट के लिए सुनेगा.

फ़ंक्शन की जगह सेट करना

Realtime Database इंस्टेंस की जगह और फ़ंक्शन की जगह के बीच की दूरी की वजह से, नेटवर्क की लेटेन्सी बढ़ सकती है. इसके अलावा, अलग-अलग इलाकों में डिप्लॉयमेंट फ़ेल हो सकता है. इन स्थितियों से बचने के लिए, फ़ंक्शन की जगह सेट करें, ताकि वह डेटाबेस इंस्टेंस की जगह से मेल खाए.

Realtime Database इवेंट मैनेज करना

फ़ंक्शन की मदद से, Realtime Database इवेंट को दो लेवल पर मैनेज किया जा सकता है; आपके पास सिर्फ़ लिखने, बनाने, अपडेट करने, या मिटाने से जुड़े इवेंट को सुनने का विकल्प होता है. इसके अलावा, किसी रेफ़रंस में होने वाले किसी भी तरह के बदलाव को सुना जा सकता है.

Realtime Database इवेंट के जवाब देने के लिए, ये हैंडलर उपलब्ध हैं:

Node.js

  • onValueWritten() Realtime Database में डेटा बनाने, अपडेट करने या मिटाने पर ट्रिगर होता है.
  • onValueCreated() सिर्फ़ तब ट्रिगर होता है, जब Realtime Database में डेटा बनाया जाता है.
  • onValueUpdated() सिर्फ़ तब ट्रिगर होता है, जब Realtime Database में डेटा अपडेट किया जाता है.
  • onValueDeleted() सिर्फ़ तब ट्रिगर होता है, जब Realtime Database में डेटा मिटाया जाता है.

Python

  • on_value_written() Realtime Database में डेटा बनाने, अपडेट करने या मिटाने पर ट्रिगर होता है.
  • on_value_created() सिर्फ़ तब ट्रिगर होता है, जब Realtime Database में डेटा बनाया जाता है.
  • on_value_updated() सिर्फ़ तब ट्रिगर होता है, जब Realtime Database में डेटा अपडेट किया जाता है.
  • on_value_deleted() सिर्फ़ तब ट्रिगर होता है, जब Realtime Database में डेटा मिटाया जाता है.

ज़रूरी मॉड्यूल इंपोर्ट करना

आपको अपने फ़ंक्शन के सोर्स में, वे SDK मॉड्यूल इंपोर्ट करने होंगे जिनका इस्तेमाल करना है. इस सैंपल के लिए, Realtime Database में लिखने के लिए, Firebase Admin SDK मॉड्यूल के साथ-साथ, एचटीटीपी और Realtime Database मॉड्यूल इंपोर्ट करना ज़रूरी है.

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()

इंस्टेंस और पाथ तय करना

यह कंट्रोल करने के लिए कि आपका फ़ंक्शन कब और कहां ट्रिगर होना चाहिए, अपने फ़ंक्शन को किसी पाथ और चाहें तो Realtime Database इंस्टेंस के साथ कॉन्फ़िगर करें. अगर कोई इंस्टेंस तय नहीं किया जाता है, तो फ़ंक्शन, फ़ंक्शन के इलाके में मौजूद सभी Realtime Database इंस्टेंस को सुनेगा. एक ही इलाके में मौजूद इंस्टेंस के किसी सबसेट पर डिप्लॉय करने के लिए, Realtime Database इंस्टेंस का पैटर्न भी तय किया जा सकता है.

उदाहरण के लिए:

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

ये पैरामीटर, आपके फ़ंक्शन को Realtime Database इंस्टेंस में किसी पाथ पर लिखने के लिए डायरेक्ट करते हैं.

पाथ की खास जानकारी, सभी ऐसे पाथ पर लिखने की कार्रवाइयों से मेल खाती है जिनमें कोई पाथ शामिल हो. इसमें, उस पाथ के नीचे मौजूद किसी भी पाथ पर लिखने की कार्रवाइयां शामिल हैं. अगर अपने फ़ंक्शन के लिए पाथ /foo/bar सेट किया जाता है, तो यह इन दोनों जगहों पर होने वाले इवेंट से मेल खाता है:

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

दोनों ही मामलों में, Firebase यह मानता है कि इवेंट /foo/bar पर हुआ है. साथ ही, इवेंट डेटा में /foo/bar पर मौजूद पुराना और नया डेटा शामिल होता है. अगर इवेंट डेटा बड़ा हो सकता है, तो अपने डेटाबेस के रूट के पास मौजूद किसी एक फ़ंक्शन के बजाय, डीपर पाथ पर कई फ़ंक्शन इस्तेमाल करें. सबसे अच्छी परफ़ॉर्मेंस के लिए, सिर्फ़ सबसे डीप लेवल पर मौजूद डेटा का अनुरोध करें.

वाइल्डकार्डिंग और कैप्चर करना

कैप्चर करने के लिए, {key}, {key=*}, {key=prefix*}, {key=*suffix} का इस्तेमाल किया जा सकता है. सिंगल-सेगमेंट वाइल्डकार्डिंग के लिए, *, prefix*, *suffix का इस्तेमाल किया जा सकता है. ध्यान दें: ** मल्टी-सेगमेंट वाइल्डकार्डिंग को दिखाता है, जिसे Realtime Database में इस्तेमाल नहीं किया जा सकता. पाथ पैटर्न समझना लेख पढ़ें.

पाथ वाइल्डकार्डिंग. किसी पाथ कॉम्पोनेंट को वाइल्डकार्ड के तौर पर सेट किया जा सकता है:

  • ऐस्टरिस्क, * का इस्तेमाल करके. उदाहरण के लिए, foo/* किसी भी चाइल्ड से मेल खाता है जो foo/ के नीचे नोड के क्रम में एक लेवल पर मौजूद है.
  • ठीक ऐस्टरिस्क, * वाला सेगमेंट इस्तेमाल करके. उदाहरण के लिए, foo/app*-us किसी भी चाइल्ड सेगमेंट से मेल खाता है, जिसमें app प्रीफ़िक्स और -us सफ़िक्स हो.foo/

वाइल्डकार्ड वाले पाथ, एक से ज़्यादा इवेंट से मेल खा सकते हैं. उदाहरण के लिए, एक बार लिखने की कार्रवाई से. को इंसर्ट करने पर,

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

पाथ "/foo/*" से दो बार मेल खाता है: एक बार "hello": "world" के साथ और दूसरी बार "firebase": "functions" के साथ.

पाथ कैप्चर करना. अपने फ़ंक्शन कोड में इस्तेमाल करने के लिए, पाथ के मिलान को नाम वाले वैरिएबल में कैप्चर किया जा सकता है. जैसे, /user/{uid}, /user/{uid=*-us}.

कैप्चर वैरिएबल की वैल्यू, आपके फ़ंक्शन के database.DatabaseEvent.params ऑब्जेक्ट में उपलब्ध होती हैं.

इंस्टेंस वाइल्डकार्डिंग. वाइल्डकार्डिंग का इस्तेमाल करके, इंस्टेंस कॉम्पोनेंट भी तय किया जा सकता है. इंस्टेंस वाइल्डकार्ड में प्रीफ़िक्स, सफ़िक्स या दोनों हो सकते हैं. जैसे, my-app-*-prod.

वाइल्डकार्ड और कैप्चर रेफ़रंस

Cloud Functions (दूसरी पीढ़ी) और Realtime Database के साथ, ref और instance तय करते समय पैटर्न का इस्तेमाल किया जा सकता है. हर ट्रिगर इंटरफ़ेस में, किसी फ़ंक्शन को स्कोप करने के लिए ये विकल्प होंगे:

तय करना ref तय करना instance व्यवहार
सिंगल (/foo/bar) तय नहीं करना हैंडलर को, फ़ंक्शन के इलाके में मौजूद सभी इंस्टेंस के लिए स्कोप करता है.
सिंगल (/foo/bar) सिंगल (‘my-new-db') हैंडलर को, फ़ंक्शन के इलाके में मौजूद खास इंस्टेंस के लिए स्कोप करता है.
सिंगल (/foo/bar) पैटर्न (‘inst-prefix*') हैंडलर को, फ़ंक्शन के इलाके में मौजूद पैटर्न से मेल खाने वाले सभी इंस्टेंस के लिए स्कोप करता है.
पैटर्न (/foo/{bar}) तय नहीं करना हैंडलर को, फ़ंक्शन के इलाके में मौजूद सभी इंस्टेंस के लिए स्कोप करता है.
पैटर्न (/foo/{bar}) सिंगल (‘my-new-db') हैंडलर को, फ़ंक्शन के इलाके में मौजूद खास इंस्टेंस के लिए स्कोप करता है.
पैटर्न (/foo/{bar}) पैटर्न (‘inst-prefix*') हैंडलर को, फ़ंक्शन के इलाके में मौजूद पैटर्न से मेल खाने वाले सभी इंस्टेंस के लिए स्कोप करता है.

इवेंट डेटा मैनेज करना

जब कोई Realtime Database इवेंट ट्रिगर होता है, तो यह आपके हैंडलर फ़ंक्शन को Event ऑब्जेक्ट पास करता है. इस ऑब्जेक्ट में data प्रॉपर्टी होती है. यह प्रॉपर्टी, डेटा बनाने और मिटाने से जुड़े इवेंट के लिए, बनाए या मिटाए गए डेटा का स्नैपशॉट दिखाती है.

इस उदाहरण में, फ़ंक्शन रेफ़रंस वाले पाथ का डेटा वापस लाता है. साथ ही, उस जगह पर मौजूद स्ट्रिंग को अपरकेस में बदलता है और उस बदली हुई स्ट्रिंग को डेटाबेस में लिखता है:

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)

पिछली वैल्यू पढ़ना

write या update इवेंट के लिए, data प्रॉपर्टी एक Change ऑब्जेक्ट होती है. इसमें दो स्नैपशॉट होते हैं, जो ट्रिगर करने वाले इवेंट से पहले और बाद में डेटा की स्थिति दिखाते हैं. Change ऑब्जेक्ट में एक before प्रॉपर्टी होती है. इसकी मदद से, यह देखा जा सकता है कि इवेंट से पहले Realtime Database में क्या सेव किया गया था. साथ ही, इसमें एक after प्रॉपर्टी होती है, जो इवेंट होने के बाद डेटा की स्थिति दिखाती है.

उदाहरण के लिए, before प्रॉपर्टी का इस्तेमाल करके यह पक्का किया जा सकता है कि फ़ंक्शन, टेक्स्ट को सिर्फ़ तब अपरकेस में बदले, जब उसे पहली बार बनाया गया हो:

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)

पुष्टि के कॉन्टेक्स्ट को ऐक्सेस करना

आरटीडीबी इवेंटआर्क इवेंट से ट्रिगर होने वाले फ़ंक्शन के लिए, पुष्टि का कॉन्टेक्स्ट, इवेंट पेलोड में शामिल होता है:

  • authtype: वह प्रिंसिपल टाइप जिसने इवेंट को ट्रिगर किया है. इसकी ये वैल्यू हो सकती हैं:
    • app_user: डेवलपर के ऐप्लिकेशन का कोई एंड यूज़र.
    • admin: कोई सेवा खाता.
    • unauthenticated: ऐसा उपयोगकर्ता जिसकी पुष्टि नहीं की गई है.
    • unknown: पुष्टि की जानकारी उपलब्ध न होने पर, डिफ़ॉल्ट वैल्यू.
  • authid: प्रिंसिपल का यूनीक आइडेंटिफ़ायर.
    • अगर authtype की वैल्यू app_user है, तो यह उपयोगकर्ता का यूआईडी है.
    • अगर authtype की वैल्यू admin है, तो यह सेवा खाता या IAM उपयोगकर्ता का ईमेल है.

यह कोड, मैसेज के टेक्स्ट को सिर्फ़ तब अपरकेस में बदलता है, जब फ़ंक्शन को ट्रिगर करने वाला उपयोगकर्ता एडमिन न हो. इसके अलावा, कोड यह भी देखता है कि मैसेज को ट्रिगर करने वाला उपयोगकर्ता, मैसेज का असल भेजने वाला है या नहीं.

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)