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

با 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

در هر دو حالت، 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);
    });