ทริกเกอร์ 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สถานที่ตั้ง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 ครั้ง ครั้งหนึ่งมี "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);
    });