ทริกเกอร์ Realtime Database


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

สร้างฟังก์ชันใหม่สําหรับเหตุการณ์ Realtime Database ด้วย functions.database ถึง ควบคุมเวลาที่ฟังก์ชันจะทริกเกอร์ ระบุตัวแฮนเดิลเหตุการณ์ ระบุเส้นทาง Realtime Database ที่จะตรวจจับเหตุการณ์

ตั้งค่าเครื่องจัดการเหตุการณ์

ฟังก์ชันช่วยให้คุณจัดการเหตุการณ์ Realtime Database ในระดับความเฉพาะเจาะจง 2 ระดับได้ โดยคุณสามารถรอฟังเฉพาะเหตุการณ์การสร้าง การอัปเดต หรือการลบ หรือจะรอฟังการเปลี่ยนแปลงทุกประเภทในเส้นทางก็ได้ 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 ระบบจะจับคู่เหตุการณ์ที่ตำแหน่งทั้ง 2 ตำแหน่งนี้

 /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}" 2 ครั้ง: 1 ครั้งด้วย "hello": "world" และอีกครั้งกับ "firebase": "functions"

จัดการข้อมูลเหตุการณ์

เมื่อจัดการเหตุการณ์ Realtime Database ออบเจ็กต์ข้อมูลที่แสดงผลคือ DataSnapshot สําหรับเหตุการณ์ onWrite หรือ onUpdate พารามิเตอร์แรกคือออบเจ็กต์ Change ที่มีภาพรวม 2 รายการซึ่งแสดงสถานะข้อมูลก่อนและหลังเหตุการณ์เรียกให้แสดง สำหรับ 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);
    });