ทริกเกอร์ 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);
    });