ทริกเกอร์ Realtime Database


ฟังก์ชันระบบคลาวด์ช่วยให้คุณสามารถจัดการเหตุการณ์ใน ฐานข้อมูลเรียลไทม์ของ Firebase โดยไม่ต้องอัปเดตโค้ดไคลเอ็นต์ Cloud Functions ช่วยให้คุณเรียกใช้การดำเนินการของ Realtime Database ด้วยการดูแลระบบเต็มรูปแบบ สิทธิ์ และดูแลให้การเปลี่ยนแปลงที่คุณทำกับ Realtime Database แต่ละรายการได้รับการประมวลผล แยกกัน คุณเปลี่ยนแปลงฐานข้อมูลเรียลไทม์ของ Firebase ได้ผ่าน DataSnapshot หรือผ่าน Admin SDK

ในวงจรทั่วไป ฟังก์ชันฐานข้อมูลเรียลไทม์ของ Firebase จะดำเนินการต่อไปนี้

  1. รอให้มีการเปลี่ยนแปลงตำแหน่งของ Realtime Database ที่เฉพาะเจาะจง
  2. ทริกเกอร์เมื่อเหตุการณ์เกิดขึ้นและทํางานของเหตุการณ์นั้น (โปรดดูฉันทำอะไรได้บ้าง) กับ Cloud Functions หรือไม่เพื่อดูตัวอย่าง กรณีการใช้งาน)
  3. รับออบเจ็กต์ข้อมูลที่มีสแนปชอตของข้อมูลที่จัดเก็บไว้ ในเอกสารที่ระบุ

ทริกเกอร์ฟังก์ชัน Realtime Database

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

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

ฟังก์ชันต่างๆ ช่วยให้คุณจัดการเหตุการณ์ Realtime Database ได้ในระดับความจำ 2 ระดับ ได้แก่ คุณสามารถฟังเฉพาะ การสร้างสรรค์ อัปเดต เหตุการณ์การลบ หรือตรวจจับการเปลี่ยนแปลงใดๆ ก็ตามในเส้นทางได้ Cloud Functions รองรับตัวแฮนเดิลเหตุการณ์สำหรับฐานข้อมูลเรียลไทม์ต่อไปนี้

  • onWrite() ซึ่งจะทริกเกอร์เมื่อมีการสร้าง อัปเดต หรือลบข้อมูลใน Realtime Database
  • onCreate() ซึ่งจะทริกเกอร์เมื่อมีการสร้างข้อมูลใหม่ใน Realtime Database
  • onUpdate() ซึ่งจะทำงานเมื่ออัปเดตข้อมูลใน Realtime Database
  • onDelete() ซึ่งจะปรากฏเมื่อลบข้อมูลออกจาก Realtime Database

ระบุอินสแตนซ์และเส้นทาง

หากต้องการควบคุมเวลาและตําแหน่งที่ฟังก์ชันจะทริกเกอร์ ให้เรียกใช้ ref(path) เพื่อระบุเส้นทาง และอาจระบุอินสแตนซ์ Realtime Database หรือไม่ก็ได้ ด้วย instance('INSTANCE_NAME') หากไม่ ระบุอินสแตนซ์ ซึ่งจะทำให้ฟังก์ชันใช้งานได้กับอินสแตนซ์ฐานข้อมูลเรียลไทม์ตามค่าเริ่มต้น ตัวอย่างโปรเจ็กต์ Firebase

  • อินสแตนซ์ฐานข้อมูลเรียลไทม์เริ่มต้น: 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');
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);
    });