Check out what’s new from Firebase at Google I/O 2022. Learn more

Kích hoạt cơ sở dữ liệu

Với Chức năng đám mây, bạn có thể xử lý các sự kiện trong Cơ sở dữ liệu thời gian thực của Firebase mà không cần cập nhật mã ứng dụng khách. Chức năng đám mây cho phép bạn chạy các hoạt động Cơ sở dữ liệu thời gian thực với đầy đủ các đặc quyền quản trị và đảm bảo rằng mỗi thay đổi đối với Cơ sở dữ liệu thời gian thực được xử lý riêng lẻ. Bạn có thể thực hiện các thay đổi trong Cơ sở dữ liệu thời gian thực của Firebase qua DataSnapshot hoặc qua SDK quản trị.

Trong một vòng đời điển hình, chức năng Cơ sở dữ liệu thời gian thực của Firebase thực hiện những việc sau:

  1. Chờ các thay đổi đối với một vị trí Cơ sở dữ liệu thời gian thực cụ thể.
  2. Kích hoạt khi một sự kiện xảy ra và thực hiện các tác vụ của nó (xem Tôi có thể làm gì với Chức năng đám mây? Để biết ví dụ về các trường hợp sử dụng).
  3. Nhận một đối tượng dữ liệu có chứa ảnh chụp nhanh của dữ liệu được lưu trữ trong tài liệu được chỉ định.

Kích hoạt chức năng Cơ sở dữ liệu thời gian thực

Tạo các chức năng mới cho các sự kiện Cơ sở dữ liệu thời gian thực với functions.database . Để kiểm soát khi nào hàm kích hoạt, hãy chỉ định một trong các trình xử lý sự kiện và chỉ định đường dẫn Cơ sở dữ liệu thời gian thực nơi nó sẽ lắng nghe các sự kiện.

Đặt trình xử lý sự kiện

Các chức năng cho phép bạn xử lý các sự kiện Cơ sở dữ liệu thời gian thực ở hai cấp độ cụ thể; bạn có thể lắng nghe cụ thể đối với chỉ các sự kiện tạo, cập nhật hoặc xóa hoặc bạn có thể nghe bất kỳ thay đổi nào dưới bất kỳ hình thức nào đối với đường dẫn. Chức năng đám mây hỗ trợ các trình xử lý sự kiện này cho Cơ sở dữ liệu thời gian thực:

  • onWrite() , kích hoạt khi dữ liệu được tạo, cập nhật hoặc xóa trong Cơ sở dữ liệu thời gian thực.
  • onCreate() , kích hoạt khi dữ liệu mới được tạo trong Cơ sở dữ liệu thời gian thực.
  • onUpdate() , kích hoạt khi dữ liệu được cập nhật trong Cơ sở dữ liệu thời gian thực.
  • onDelete() , kích hoạt khi dữ liệu bị xóa khỏi Cơ sở dữ liệu thời gian thực.

Chỉ định phiên bản và đường dẫn

Để kiểm soát thời điểm và vị trí hàm của bạn sẽ kích hoạt, hãy gọi ref(path) để chỉ định một đường dẫn và tùy chọn chỉ định đối tượng Cơ sở dữ liệu thời gian thực với instance('INSTANCE_NAME') . Nếu bạn không chỉ định một phiên bản, thì hàm sẽ triển khai phiên bản Realtime Database mặc định cho dự án Firebase. Ví dụ:

  • Phiên bản Cơ sở dữ liệu thời gian thực mặc định: functions.database.ref('/foo/bar')
  • Phiên bản có tên "my-app-db-2": functions.database.instance('my-app-db-2').ref('/foo/bar')

Các phương thức này hướng chức năng của bạn xử lý việc ghi tại một đường dẫn nhất định trong phiên bản Cơ sở dữ liệu thời gian thực. Thông số kỹ thuật của đường dẫn khớp với tất cả các lần ghi chạm vào một đường dẫn, bao gồm cả các lần ghi xảy ra ở bất kỳ đâu bên dưới đường dẫn đó. Nếu bạn đặt đường dẫn cho hàm của mình là /foo/bar , thì nó sẽ khớp với các sự kiện ở cả hai vị trí sau:

 /foo/bar
 /foo/bar/baz/really/deep/path

Trong cả hai trường hợp, Firebase diễn giải rằng sự kiện xảy ra tại /foo/bar và dữ liệu sự kiện bao gồm dữ liệu cũ và mới tại /foo/bar . Nếu dữ liệu sự kiện có thể lớn, hãy xem xét sử dụng nhiều hàm ở các đường dẫn sâu hơn thay vì một hàm duy nhất gần gốc cơ sở dữ liệu của bạn. Để có hiệu suất tốt nhất, chỉ yêu cầu dữ liệu ở mức sâu nhất có thể.

Bạn có thể chỉ định một thành phần đường dẫn dưới dạng ký tự đại diện bằng cách bao quanh nó bằng dấu ngoặc nhọn; ref('foo/{bar}') khớp với bất kỳ con nào của /foo . Giá trị của các thành phần đường dẫn ký tự đại diện này có sẵn trong đối tượng EventContext.params của hàm của bạn. Trong ví dụ này, giá trị có sẵn dưới dạng context.params.bar .

Các đường dẫn có ký tự đại diện có thể khớp với nhiều sự kiện từ một lần ghi. Một phụ trang của

{
  "foo": {
    "hello": "world",
    "firebase": "functions"
  }
}

khớp với đường dẫn "/foo/{bar}" hai lần: một lần với "hello": "world" và một lần nữa với "firebase": "functions" .

Xử lý dữ liệu sự kiện

Khi xử lý sự kiện Cơ sở dữ liệu thời gian thực, đối tượng dữ liệu được trả về là một DataSnapshot dữ liệu. Đối với sự kiện onWrite hoặc onUpdate , tham số đầu tiên là đối tượng Change chứa hai ảnh chụp nhanh đại diện cho trạng thái dữ liệu trước và sau sự kiện kích hoạt. Đối với các sự kiện onCreateonDelete , đối tượng dữ liệu được trả về là ảnh chụp nhanh của dữ liệu được tạo hoặc xóa.

Trong ví dụ này, hàm truy xuất ảnh chụp nhanh cho đường dẫn được chỉ định dưới dạng snap , chuyển đổi chuỗi tại vị trí đó thành chữ hoa và ghi chuỗi đã sửa đổi đó vào cơ sở dữ liệu:

// 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);
    });

Truy cập thông tin xác thực người dùng

Từ EventContext.authEventContext.authType , bạn có thể truy cập thông tin người dùng, bao gồm cả quyền, cho người dùng đã kích hoạt một chức năng. Điều này có thể hữu ích để thực thi các quy tắc bảo mật, cho phép chức năng của bạn hoàn thành các hoạt động khác nhau dựa trên cấp độ quyền của người dùng:

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);
      }
    });

Ngoài ra, bạn có thể tận dụng thông tin xác thực người dùng để "mạo danh" người dùng và thực hiện các thao tác ghi thay mặt người dùng. Đảm bảo xóa phiên bản ứng dụng như được hiển thị bên dưới để ngăn các vấn đề về đồng thời:

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));
      });
    });

Đọc giá trị trước đó

Đối tượng Change có thuộc tính before cho phép bạn kiểm tra những gì đã được lưu vào Cơ sở dữ liệu thời gian thực trước sự kiện. Thuộc tính before trả về một DataSnapshot trong đó tất cả các phương thức (ví dụ: val()exists() ) tham chiếu đến giá trị trước đó. Bạn có thể đọc lại giá trị mới bằng cách sử dụng DataSnapshot ban đầu hoặc đọc thuộc tính after . Thuộc tính này trên bất kỳ Change nào là một DataSnapshot dữ liệu khác đại diện cho trạng thái của dữ liệu sau khi sự kiện xảy ra.

Ví dụ: thuộc tính before có thể được sử dụng để đảm bảo hàm chỉ viết hoa văn bản khi nó được tạo lần đầu tiên:

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);
    });