Memperluas fungsi Cloud Firestore dengan Cloud Functions (generasi ke-2)

Dengan Cloud Functions, Anda dapat men-deploy kode untuk menangani peristiwa yang dipicu oleh perubahan pada database Cloud Firestore. Dengan begitu, Anda dapat menambahkan fungsionalitas sisi server ke aplikasi dengan mudah tanpa harus menjalankan server Anda sendiri.

Cloud Functions (generasi ke-2)

Dengan dukungan Cloud Run dan Eventarc, Cloud Functions for Firebase (generasi ke-2) memberi Anda infrastruktur yang lebih andal kontrol lanjutan atas performa dan skalabilitas, serta kontrol runtime fungsi yang lebih besar. Untuk mengetahui informasi selengkapnya tentang generasi ke-2, lihat Cloud Functions for Firebase (generasi ke-2). Untuk melihat informasi selengkapnya tentang generasi ke-1, lihat Memperluas Cloud Firestore dengan Cloud Functions.

Pemicu fungsi Cloud Firestore

Cloud Functions for Firebase SDK mengekspor pemicu peristiwa Cloud Firestore berikut untuk memungkinkan Anda membuat pengendali yang terkait dengan peristiwa Cloud Firestore tertentu:

Node.js

Jenis Peristiwa Pemicu
onDocumentCreated Dipicu saat dokumen ditulisi untuk pertama kalinya.
onDocumentUpdated Dipicu saat dokumen sudah ada dan nilainya berubah.
onDocumentDeleted Dipicu saat dokumen dihapus.
onDocumentWritten Dipicu saat onDocumentCreated, onDocumentUpdated, atau onDocumentDeleted dipicu.
onDocumentCreatedWithAuthContext onDocumentCreated dengan informasi autentikasi tambahan
onDocumentWrittenWithAuthContext onDocumentWritten dengan informasi autentikasi tambahan
onDocumentDeletedWithAuthContext onDocumentDeleted dengan informasi autentikasi tambahan
onDocumentUpdatedWithAuthContext onDocumentUpdated dengan informasi autentikasi tambahan

Python (pratinjau)

Jenis Peristiwa Pemicu
on_document_created Dipicu saat dokumen ditulisi untuk pertama kalinya.
on_document_updated Dipicu saat dokumen sudah ada dan nilainya berubah.
on_document_deleted Dipicu saat dokumen dihapus.
on_document_written Dipicu saat on_document_created, on_document_updated, atau on_document_deleted dipicu.
on_document_created_with_auth_context on_document_created dengan informasi autentikasi tambahan
on_document_updated_with_auth_context on_document_updated dengan informasi autentikasi tambahan
on_document_deleted_with_auth_context on_document_deleted dengan informasi autentikasi tambahan
on_document_written_with_auth_context on_document_written dengan informasi autentikasi tambahan

Peristiwa Cloud Firestore hanya dipicu jika ada perubahan dokumen. Pembaruan terhadap dokumen Cloud Firestore yang tidak mengubah data (penulisan tanpa pengoperasian), tidak menghasilkan peristiwa penulisan atau pembaruan. Peristiwa tidak dapat ditambahkan ke kolom tertentu.

Jika Anda belum mengaktifkan project untuk Cloud Functions for Firebase, baca Memulai Cloud Functions for Firebase (generasi ke-2) untuk mengonfigurasi dan menyiapkan Cloud Functions Anda untuk project Firebase.

Menulis fungsi yang dipicu oleh Cloud Firestore

Mendefinisikan pemicu fungsi

Untuk mendefinisikan pemicu Cloud Firestore, tentukan jalur dokumen dan jenis peristiwa:

Node.js

import {
  onDocumentWritten,
  onDocumentCreated,
  onDocumentUpdated,
  onDocumentDeleted,
  Change,
  FirestoreEvent
} from "firebase-functions/v2/firestore";

exports.myfunction = onDocumentWritten("my-collection/{docId}", (event) => {
   /* ... */ 
});

Python (pratinjau)

from firebase_functions.firestore_fn import (
  on_document_created,
  on_document_deleted,
  on_document_updated,
  on_document_written,
  Event,
  Change,
  DocumentSnapshot,
)

@on_document_created(document="users/{userId}")
def myfunction(event: Event[DocumentSnapshot]) -> None:

Jalur dokumen dapat merujuk pada dokumen tertentu atau pola karakter pengganti.

Menentukan satu dokumen

Jika ingin memicu suatu peristiwa untuk perubahan apa pun pada dokumen tertentu, Anda dapat menggunakan fungsi berikut.

Node.js

import {
  onDocumentWritten,
  Change,
  FirestoreEvent
} from "firebase-functions/v2/firestore";

exports.myfunction = onDocumentWritten("users/marie", (event) => {
  // Your code here
});

Python (pratinjau)

from firebase_functions.firestore_fn import (
  on_document_written,
  Event,
  Change,
  DocumentSnapshot,
)

@on_document_written(document="users/marie")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:

Menentukan grup dokumen menggunakan karakter pengganti

Jika ingin menambahkan pemicu ke grup dokumen, seperti dokumen dalam koleksi tertentu, gunakan {wildcard} sebagai pengganti ID dokumen:

Node.js

import {
  onDocumentWritten,
  Change,
  FirestoreEvent
} from "firebase-functions/v2/firestore";

exports.myfunction = onDocumentWritten("users/{userId}", (event) => {
  // If we set `/users/marie` to {name: "Marie"} then
  // event.params.userId == "marie"
  // ... and ...
  // event.data.after.data() == {name: "Marie"}
});

Python (pratinjau)

from firebase_functions.firestore_fn import (
  on_document_written,
  Event,
  Change,
  DocumentSnapshot,
)

@on_document_written(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
  # If we set `/users/marie` to {name: "Marie"} then
  event.params["userId"] == "marie"  # True
  # ... and ...
  event.data.after.to_dict() == {"name": "Marie"}  # True

Dalam contoh ini, saat kolom dalam dokumen pada users diubah, sistem akan mencocokkannya dengan karakter pengganti yang disebut userId.

Jika dokumen dalam users memiliki subkoleksi, dan kolom di salah satu dokumen subkoleksi tersebut diubah, karakter pengganti userId tidak akan terpicu.

Kecocokan karakter pengganti diekstrak dari jalur dokumen dan disimpan ke dalam event.params. Anda dapat mendefinisikan sebanyak mungkin karakter pengganti yang diinginkan untuk menggantikan ID dokumen atau koleksi eksplisit, misalnya:

Node.js

import {
  onDocumentWritten,
  Change,
  FirestoreEvent
} from "firebase-functions/v2/firestore";

exports.myfunction = onDocumentWritten("users/{userId}/{messageCollectionId}/{messageId}", (event) => {
    // If we set `/users/marie/incoming_messages/134` to {body: "Hello"} then
    // event.params.userId == "marie";
    // event.params.messageCollectionId == "incoming_messages";
    // event.params.messageId == "134";
    // ... and ...
    // event.data.after.data() == {body: "Hello"}
});

Python (pratinjau)

from firebase_functions.firestore_fn import (
  on_document_written,
  Event,
  Change,
  DocumentSnapshot,
)

@on_document_written(document="users/{userId}/{messageCollectionId}/{messageId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
  # If we set `/users/marie/incoming_messages/134` to {body: "Hello"} then
  event.params["userId"] == "marie"  # True
  event.params["messageCollectionId"] == "incoming_messages"  # True
  event.params["messageId"] == "134"  # True
  # ... and ...
  event.data.after.to_dict() == {"body": "Hello"}

Pemicu Anda harus selalu menunjuk ke sebuah dokumen, meskipun Anda menggunakan karakter pengganti. Misalnya, users/{userId}/{messageCollectionId} tidak valid karena {messageCollectionId} adalah sebuah koleksi. Namun, users/{userId}/{messageCollectionId}/{messageId} valid karena {messageId} akan selalu mengarah ke dokumen.

Pemicu Peristiwa

Memicu fungsi saat dokumen baru dibuat

Anda dapat memicu fungsi agar aktif setiap kali ada dokumen baru yang dibuat dalam koleksi. Fungsi contoh ini akan terpicu setiap kali profil pengguna baru ditambahkan:

Node.js

import {
  onDocumentCreated,
  Change,
  FirestoreEvent
} from "firebase-functions/v2/firestore";

exports.createuser = onDocumentCreated("users/{userId}", (event) => {
    // Get an object representing the document
    // e.g. {'name': 'Marie', 'age': 66}
    const snapshot = event.data;
    if (!snapshot) {
        console.log("No data associated with the event");
        return;
    }
    const data = snapshot.data();

    // access a particular field as you would any JS property
    const name = data.name;

    // perform more operations ...
});

Untuk informasi autentikasi tambahan, gunakan onDocumentCreatedWithAuthContext.

Python (pratinjau)

from firebase_functions.firestore_fn import (
  on_document_created,
  Event,
  DocumentSnapshot,
)

@on_document_created(document="users/{userId}")
def myfunction(event: Event[DocumentSnapshot]) -> None:
  # Get a dictionary representing the document
  # e.g. {'name': 'Marie', 'age': 66}
  new_value = event.data.to_dict()

  # Access a particular field as you would any dictionary
  name = new_value["name"]

  # Perform more operations ...

Memicu fungsi saat dokumen diperbarui

Anda juga dapat memicu fungsi agar aktif saat dokumen diperbarui. Fungsi contoh ini akan aktif jika pengguna mengubah profilnya:

Node.js

import {
  onDocumentUpdated,
  Change,
  FirestoreEvent
} from "firebase-functions/v2/firestore";

exports.updateuser = onDocumentUpdated("users/{userId}", (event) => {
    // Get an object representing the document
    // e.g. {'name': 'Marie', 'age': 66}
    const newValue = event.data.after.data();

    // access a particular field as you would any JS property
    const name = newValue.name;

    // perform more operations ...
});

Untuk informasi autentikasi tambahan, gunakan onDocumentUpdatedWithAuthContext.

Python (pratinjau)

from firebase_functions.firestore_fn import (
  on_document_updated,
  Event,
  Change,
  DocumentSnapshot,
)

@on_document_updated(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
  # Get a dictionary representing the document
  # e.g. {'name': 'Marie', 'age': 66}
  new_value = event.data.after.to_dict()

  # Access a particular field as you would any dictionary
  name = new_value["name"]

  # Perform more operations ...

Memicu fungsi saat dokumen dihapus

Anda juga dapat memicu fungsi saat dokumen dihapus. Fungsi contoh ini akan aktif ketika pengguna menghapus profilnya:

Node.js

import {
  onDocumentDeleted,
  Change,
  FirestoreEvent
} from "firebase-functions/v2/firestore";

exports.deleteuser = onDocumentDeleted("users/{userId}", (event) => {
    // Get an object representing the document
    // e.g. {'name': 'Marie', 'age': 66}
    const snap =  event.data;
    const data =  snap.data();

    // perform more operations ...
});

Untuk informasi autentikasi tambahan, gunakan onDocumentDeletedWithAuthContext.

Python (pratinjau)

from firebase_functions.firestore_fn import (
  on_document_deleted,
  Event,
  DocumentSnapshot,
)

@on_document_deleted(document="users/{userId}")
def myfunction(event: Event[DocumentSnapshot|None]) -> None:
  # Perform more operations ...

Memicu fungsi untuk semua perubahan pada dokumen

Jika tidak mementingkan jenis peristiwa yang diaktifkan, Anda dapat memproses semua perubahan dalam dokumen Cloud Firestore menggunakan pemicu peristiwa "dokumen yang ditulis". Fungsi contoh ini akan diaktifkan jika pengguna dibuat, diperbarui, atau dihapus:

Node.js

import {
  onDocumentWritten,
  Change,
  FirestoreEvent
} from "firebase-functions/v2/firestore";

exports.modifyuser = onDocumentWritten("users/{userId}", (event) => {
    // Get an object with the current document values.
    // If the document does not exist, it was deleted
    const document =  event.data.after.data();

    // Get an object with the previous document values
    const previousValues =  event.data.before.data();

    // perform more operations ...
});

Untuk informasi autentikasi tambahan, gunakan onDocumentWrittenWithAuthContext.

Python (pratinjau)

from firebase_functions.firestore_fn import (
  on_document_written,
  Event,
  Change,
  DocumentSnapshot,
)

@on_document_written(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot | None]]) -> None:
  # Get an object with the current document values.
  # If the document does not exist, it was deleted.
  document = (event.data.after.to_dict()
              if event.data.after is not None else None)

  # Get an object with the previous document values.
  # If the document does not exist, it was newly created.
  previous_values = (event.data.before.to_dict()
                     if event.data.before is not None else None)

  # Perform more operations ...

Membaca dan Menulis Data

Jika dipicu, suatu fungsi akan menghasilkan snapshot data yang terkait dengan peristiwa tersebut. Anda dapat menggunakan snapshot ini untuk membaca atau menulis dokumen yang memicu peristiwa tersebut, atau menggunakan Firebase Admin SDK untuk mengakses bagian lain database Anda.

Data Peristiwa

Membaca Data

Saat sebuah fungsi dipicu, Anda mungkin ingin mendapatkan data dari dokumen sesudah atau sebelum pembaruan. Anda bisa mendapatkan data sebelumnya menggunakan event.data.before, yang berisi snapshot dokumen sebelum pembaruan. Demikian pula, event.data.after berisi status snapshot dokumen setelah pembaruan.

Node.js

exports.updateuser2 = onDocumentUpdated("users/{userId}", (event) => {
    // Get an object with the current document values.
    // If the document does not exist, it was deleted
    const newValues =  event.data.after.data();

    // Get an object with the previous document values
    const previousValues =  event.data.before.data();
});

Python (pratinjau)

@on_document_updated(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
  # Get an object with the current document values.
  new_value = event.data.after.to_dict()

  # Get an object with the previous document values.
  prev_value = event.data.before.to_dict()

Anda dapat mengakses properti sebagaimana Anda mengakses properti pada objek lainnya. Atau, Anda dapat menggunakan fungsi get untuk mengakses kolom tertentu:

Node.js

// Fetch data using standard accessors
const age = event.data.after.data().age;
const name = event.data.after.data()['name'];

// Fetch data using built in accessor
const experience = event.data.after.data.get('experience');

Python (pratinjau)

# Get the value of a single document field.
age = event.data.after.get("age")

# Convert the document to a dictionary.
age = event.data.after.to_dict()["age"]

Menulis Data

Setiap pemanggilan fungsi dikaitkan dengan dokumen tertentu dalam database Cloud Firestore. Anda dapat mengakses dokumen tersebut di ringkasan yang ditampilkan ke fungsi Anda.

Referensi dokumen menyertakan metode seperti update(), set(), dan remove() sehingga Anda dapat mengubah dokumen yang memicu fungsi.

Node.js

import { onDocumentUpdated } from "firebase-functions/v2/firestore";

exports.countnamechanges = onDocumentUpdated('users/{userId}', (event) => {
  // Retrieve the current and previous value
  const data = event.data.after.data();
  const previousData = event.data.before.data();

  // We'll only update if the name has changed.
  // This is crucial to prevent infinite loops.
  if (data.name == previousData.name) {
    return null;
  }

  // Retrieve the current count of name changes
  let count = data.name_change_count;
  if (!count) {
    count = 0;
  }

  // Then return a promise of a set operation to update the count
  return data.after.ref.set({
    name_change_count: count + 1
  }, {merge: true});

});

Python (pratinjau)

@on_document_updated(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
  # Get the current and previous document values.
  new_value = event.data.after
  prev_value = event.data.before

  # We'll only update if the name has changed.
  # This is crucial to prevent infinite loops.
  if new_value.get("name") == prev_value.get("name"):
      return

  # Retrieve the current count of name changes
  count = new_value.to_dict().get("name_change_count", 0)

  # Update the count
  new_value.reference.update({"name_change_count": count + 1})

Mengakses informasi autentikasi pengguna

Jika menggunakan salah satu jenis peristiwa berikut, Anda dapat mengakses informasi autentikasi pengguna tentang akun utama yang memicu peristiwa tersebut. Informasi ini merupakan tambahan untuk informasi yang ditampilkan dalam peristiwa dasar.

Node.js

  • onDocumentCreatedWithAuthContext
  • onDocumentWrittenWithAuthContext
  • onDocumentDeletedWithAuthContext
  • onDocumentUpdatedWithAuthContext

Python (pratinjau)

  • on_document_created_with_auth_context
  • on_document_updated_with_auth_context
  • on_document_deleted_with_auth_context
  • on_document_written_with_auth_context

Untuk informasi tentang data yang tersedia dalam konteks autentikasi, lihat Konteks Autentikasi. Contoh berikut menunjukkan cara mengambil informasi autentikasi:

Node.js

import { onDocumentWrittenWithAuthContext } from "firebase-functions/v2/firestore"

exports.syncUser = onDocumentWrittenWithAuthContext("users/{userId}", (event) => {
    const snapshot = event.data.after;
    if (!snapshot) {
        console.log("No data associated with the event");
        return;
    }
    const data = snapshot.data();

    // retrieve auth context from event
    const { authType, authId } = event;

    let verified = false;
    if (authType === "system") {
      // system-generated users are automatically verified
      verified = true;
    } else if (authType === "unknown" || authType === "unauthenticated") {
      // admin users from a specific domain are verified
      if (authId.endsWith("@example.com")) {
        verified = true;
      }
    }

    return data.after.ref.set({
        created_by: authId,
        verified,
    }, {merge: true}); 
}); 

Python (pratinjau)

@on_document_updated_with_auth_context(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:

  # Get the current and previous document values.
  new_value = event.data.after
  prev_value = event.data.before

  # Get the auth context from the event
  user_auth_type = event.auth_type
  user_auth_id = event.auth_id

Data di luar peristiwa pemicu

Cloud Functions dijalankan dalam lingkungan tepercaya. Alat tersebut diotorisasi sebagai akun layanan di project Anda, dan Anda dapat melakukan pembacaan dan penulisan menggunakan Firebase Admin SDK:

Node.js

const { initializeApp } = require('firebase-admin/app');
const { getFirestore, Timestamp, FieldValue } = require('firebase-admin/firestore');

initializeApp();
const db = getFirestore();

exports.writetofirestore = onDocumentWritten("some/doc", (event) => {
    db.doc('some/otherdoc').set({ ... });
  });

  exports.writetofirestore = onDocumentWritten('users/{userId}', (event) => {
    db.doc('some/otherdoc').set({
      // Update otherdoc
    });
  });

Python (pratinjau)

from firebase_admin import firestore, initialize_app
import google.cloud.firestore

initialize_app()

@on_document_written(document="some/doc")
def myfunction(event: Event[Change[DocumentSnapshot | None]]) -> None:
  firestore_client: google.cloud.firestore.Client = firestore.client()
  firestore_client.document("another/doc").set({
      # ...
  })

Keterbatasan

Perhatikan batasan berikut untuk pemicu Cloud Firestore untuk Cloud Functions:

  • Cloud Functions (generasi ke-1) menjadi prasyarat database "(default)" yang ada dalam mode native Firestore. Lingkungan ini tidak mendukung mode Datastore atau database bernama Cloud Firestore. Gunakan Cloud Functions (generasi ke-2) untuk mengonfigurasi peristiwa dalam kasus tersebut.
  • Pengurutan tidak dijamin. Perubahan cepat dapat memicu pemanggilan fungsi dalam urutan yang tidak terduga.
  • Peristiwa dikirim setidaknya satu kali, tetapi satu peristiwa dapat menghasilkan beberapa pemanggilan fungsi. Hindari mengandalkan mekanisme tepat satu kali, dan tulis fungsi idempoten.
  • Cloud Firestore dalam mode Datastore memerlukan Cloud Functions (generasi ke-2). Cloud Functions (generasi ke-1) tidak mendukung mode Datastore.
  • Pemicu dikaitkan dengan satu database. Anda tidak dapat membuat pemicu yang cocok dengan beberapa database.
  • Menghapus database tidak secara otomatis menghapus pemicu untuk database tersebut. Pemicu berhenti mengirim peristiwa, tetapi akan tetap ada sampai Anda menghapus pemicu.
  • Jika peristiwa yang cocok melebihi ukuran permintaan maksimum, peristiwa tersebut mungkin tidak akan dikirim ke Cloud Functions (generasi ke-1).
    • Peristiwa yang tidak terkirim karena ukuran permintaan akan dicatat dalam log platform dan diperhitungkan terhadap penggunaan log untuk project.
    • Anda dapat menemukan log ini di Logs Explorer dengan pesan "Event dapat mengirim ke Cloud function karena ukuran melebihi batas untuk generasi ke-1..." dengan tingkat keparahan error. Anda dapat menemukan nama fungsi di bawah kolom functionName. Jika kolom receiveTimestamp masih berada dalam waktu satu jam dari sekarang, Anda dapat menyimpulkan konten peristiwa yang sebenarnya dengan membaca dokumen yang dimaksud menggunakan snapshot sebelum dan setelah stempel waktu.
    • Untuk menghindari peristiwa seperti ini, Anda dapat:
      • Melakukan migrasi dan upgrade ke Cloud Functions (generasi ke-2)
      • Memperkecil dokumen
      • Menghapus Cloud Functions yang dimaksud
    • Anda dapat menonaktifkan logging itu sendiri menggunakan pengecualian, tetapi perhatikan bahwa peristiwa yang melanggar tidak akan dikirim.