ทริกเกอร์ Realtime Database (รุ่นที่ 1)

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 ครั้ง โดยครั้งแรกจับคู่กับ "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);
    });