راه‌اندازهای پایگاه داده بی‌درنگ

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

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

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

می‌توانید تابعی را در پاسخ به نوشتن، ایجاد، به‌روزرسانی، یا حذف گره‌های پایگاه داده در 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 حذف شود.

پایتون

  • on_value_written() وقتی داده‌ها در Realtime Database ایجاد، به‌روزرسانی، یا حذف می‌شوند، راه‌اندازی می‌شود.
  • on_value_created() فقط زمانی راه‌اندازی می‌شود که داده‌ها در Realtime Database ایجاد شود.
  • on_value_updated() فقط زمانی راه‌اندازی می‌شود که داده‌ها در Realtime Database به‌روزرسانی شود.
  • on_value_deleted() فقط زمانی راه‌اندازی می‌شود که داده‌ها در Realtime Database حذف شود.

وارد کردن واحدهای ضروری

در منبع تابع، باید واحدهای SDK را که می‌خواهید استفاده کنید وارد کنید. برای این نمونه، لازم است واحدهای HTTP و 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();

پایتون

# 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) => {
    // …
  }
);

پایتون

# 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 با هریک از بخش‌های کودک زیر foo/ با پیشوند app و پسوند -us مطابقت دارد.

مسیرهای دارای نویسه‌های عام می‌توانند با چندین رویداد از، برای مثال، یک نوشتن واحد مطابقت داشته باشند. برش

{
  "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);
    },
);

پایتون

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

پایتون

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

دسترسی به بافت اصالت‌سنجی

برای توابعی که با رویدادهای RTDB eventarc راه‌اندازی می‌شوند، زمینه اصالت‌سنجی در بار رویداد گنجانده می‌شود:

  • authtype: نوع اصلی که رویداد را راه‌اندازی کرده است. مقادیر ممکن عبارت‌اند از:
    • app_user: کاربر نهایی برنامه توسعه‌دهنده.
    • ‫admin: حساب سرویس.
    • ‫unauthenticated: کاربر اصالت‌سنجی‌نشده.
    • ‫unknown: پیش‌فرض وقتی اطلاعات اصالت‌سنجی دردسترس نباشد.
  • authid: شناسه یکتای اصلی.
    • اگر authtype برابر با app_user باشد، این UID کاربر است.
    • اگر 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);
});

پایتون

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)