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

با Cloud Functions، می‌توانید رویدادها را در Firebase Realtime Database بدون نیاز به به‌روزرسانی کد کارخواه مدیریت کنید. ‫Cloud Functions به شما امکان می‌دهد Realtime Database عملیات را با امتیازات کامل سرپرستی اجرا کنید و تضمین می‌کند که هر تغییر در Realtime Database به‌صورت جداگانه پردازش شود. می‌توانید Firebase Realtime Database تغییر را ازطریق DataSnapshot یا ازطریق کیت توسعه نرم‌افزار سرپرست انجام دهید.

در یک چرخه عمر معمولی، یک تابع Firebase Realtime Database کارهای زیر را انجام می‌دهد:

  1. منتظر تغییرات در مکان Realtime Database خاصی می‌ماند.
  2. وقتی رویدادی رخ می‌دهد راه‌اندازی می‌شود و وظایفش را انجام می‌دهد (به با Cloud Functions چه کارهایی می‌توانم انجام دهم؟ مراجعه کنید) برای مثال‌های موارد استفاده).
  3. شیء داده‌ای را دریافت می‌کند که حاوی عکس آنی از داده‌های ذخیره‌شده در سند مشخص‌شده است.

راه‌اندازی تابع Realtime Database

برای Realtime Database رویداد با functions.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);
    });