راه‌اندازهای پایگاه داده بیدرنگ (نسل اول)

با استفاده از 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') مشخص کنید. اگر نمونه‌ای مشخص نکنید، تابع به نمونه پیش‌فرض Realtime Database برای پروژه Firebase منتقل می‌شود. به عنوان مثال:

  • نمونه 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

در هر صورت، فایربیس تفسیر می‌کند که رویداد در /foo/bar رخ می‌دهد و داده‌های رویداد شامل داده‌های قدیمی و جدید در /foo/bar است. اگر داده‌های رویداد ممکن است بزرگ باشند، به جای یک تابع واحد در نزدیکی ریشه پایگاه داده خود، استفاده از چندین تابع در مسیرهای عمیق‌تر را در نظر بگیرید. برای بهترین عملکرد، فقط داده‌ها را در عمیق‌ترین سطح ممکن درخواست کنید.

شما می‌توانید با قرار دادن یک آکولاد در اطراف یک کامپوننت مسیر، آن را به عنوان یک کاراکتر جایگزین (wildcard) مشخص کنید؛ ref('foo/{bar}') با هر فرزندی از /foo مطابقت دارد. مقادیر این اجزای مسیر جایگزین (wildcard) در شیء EventContext.params تابع شما موجود است. در این مثال، مقدار به صورت context.params.bar در دسترس است.

مسیرهای دارای wildcard می‌توانند با چندین رویداد از یک نوشتن واحد مطابقت داشته باشند. درج یک

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

مسیر "/foo/{bar}" را دو بار تطبیق می‌دهد: یک بار با "hello": "world" و بار دیگر با "firebase": "functions" .

مدیریت داده‌های رویداد

هنگام مدیریت یک رویداد Realtime Database ، شیء داده‌ای که برگردانده می‌شود یک DataSnapshot است. برای رویدادهای onWrite یا onUpdate ، اولین پارامتر یک شیء Change است که شامل دو snapshot است که وضعیت داده‌ها را قبل و بعد از رویداد فعال‌سازی نشان می‌دهند. برای رویدادهای onCreate و onDelete ، شیء داده‌ای که برگردانده می‌شود یک snapshot از داده‌های ایجاد شده یا حذف شده است.

در این مثال، تابع، snapshot مسیر مشخص شده را بازیابی می‌کند، رشته موجود در آن مکان را به حروف بزرگ تبدیل می‌کند و آن رشته اصلاح شده را در پایگاه داده می‌نویسد:

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