रीयलटाइम डेटाबेस ट्रिगर (पहली जनरेशन)

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

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

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

किसी Realtime Database फ़ंक्शन को ट्रिगर करना

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

इवेंट हैंडलर सेट करना

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

  • onWrite(), जो Realtime Database में डेटा बनाने, अपडेट करने या मिटाने पर ट्रिगर होता है.
  • onCreate(), जो Realtime Database में नया डेटा बनाने पर ट्रिगर होता है.
  • onUpdate(), जो Realtime Database में डेटा अपडेट होने पर ट्रिगर होता है .
  • onDelete(), जो Realtime Database से डेटा मिटाने पर ट्रिगर होता है .

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

यह तय करने के लिए कि आपका फ़ंक्शन कब और कहां ट्रिगर होना चाहिए, पाथ तय करने के लिए ref(path) को कॉल करें. इसके अलावा, Realtime Database इंस्टेंस की मदद से, instance('INSTANCE_NAME') तय करें. अगर कोई इंस्टेंस तय नहीं किया जाता है, तो फ़ंक्शन को Firebase प्रोजेक्ट के लिए, डिफ़ॉल्ट Realtime Database इंस्टेंस पर डिप्लॉय किया जाता है. उदाहरण के लिए:

  • डिफ़ॉल्ट Realtime Database इंस्टेंस: functions.database.ref('/foo/bar')
  • "my-app-db-2" नाम का इंस्टेंस: functions.database.instance('my-app-db-2').ref('/foo/bar')

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

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

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

पाथ कॉम्पोनेंट को कर्ली ब्रैकेट में रखकर, वाइल्डकार्ड के तौर पर तय किया जा सकता है. ref('foo/{bar}') किसी भी चाइल्ड से मेल खाता है /foo. इन वाइल्डकार्ड पाथ कॉम्पोनेंट की वैल्यू, आपके फ़ंक्शन के EventContext.params ऑब्जेक्ट में उपलब्ध होती हैं. इस उदाहरण में, वैल्यू context.params.bar के तौर पर उपलब्ध है.

वाइल्डकार्ड वाले पाथ, एक ही राइट ऑपरेशन से कई इवेंट से मेल खा सकते हैं. को इंसर्ट करने पर

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

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

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

जब किसी Realtime Database इवेंट को मैनेज किया जाता है, तो लौटाया गया डेटा ऑब्जेक्ट एक DataSnapshot होता है. onWrite या onUpdate इवेंट के लिए, पहला पैरामीटर Change ऑब्जेक्ट होता है. इसमें दो स्नैपशॉट होते हैं. ये स्नैपशॉट, ट्रिगर करने वाले इवेंट से पहले और बाद में डेटा की स्थिति दिखाते हैं. onCreate और onDelete इवेंट के लिए, लौटाया गया डेटा ऑब्जेक्ट, बनाए या मिटाए गए डेटा का स्नैपशॉट होता है.

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

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

उपयोगकर्ता की पुष्टि करने से जुड़ी जानकारी ऐक्सेस करना

EventContext.auth और EventContext.authType से, उपयोगकर्ता की जानकारी ऐक्सेस की जा सकती है. इसमें, किसी फ़ंक्शन को ट्रिगर करने वाले उपयोगकर्ता की अनुमतियां भी शामिल होती हैं. यह सुरक्षा के नियमों को लागू करने के लिए काम आ सकता है. इससे, आपकी अनुमति के लेवल के आधार पर, आपका फ़ंक्शन अलग-अलग कार्रवाइयां पूरी कर सकता है:

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

इसके अलावा, उपयोगकर्ता की पुष्टि करने से जुड़ी जानकारी का इस्तेमाल करके, किसी उपयोगकर्ता के तौर पर "इंपर्सनेट" किया जा सकता है. साथ ही, उपयोगकर्ता की ओर से राइट ऑपरेशन किए जा सकते हैं. कॉन्करेंसी से जुड़ी समस्याओं से बचने के लिए, ऐप्लिकेशन इंस्टेंस को मिटाना न भूलें. इसके लिए, नीचे दिया गया तरीका अपनाएं:

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

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

Change ऑब्जेक्ट में before प्रॉपर्टी होती है. इससे यह देखा जा सकता है कि इवेंट से Realtime Database पहले क्या सेव किया गया था. before प्रॉपर्टी, DataSnapshot लौटाती है. इसमें सभी तरीके (उदाहरण के लिए, val() और exists()) पिछली वैल्यू को रेफ़र करते हैं. ओरिजनल DataSnapshot का इस्तेमाल करके या afterप्रॉपर्टी को पढ़कर, नई वैल्यू को फिर से पढ़ा जा सकता है. किसी भी Change पर मौजूद यह प्रॉपर्टी, एक और DataSnapshot होती है. यह इवेंट होने के बाद डेटा की स्थिति दिखाती है.

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

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