Memigrasikan Firebase Extensions ke Cloud Functions

Panduan ini menunjukkan cara memigrasikan ekstensi Anda dari lingkungan Firebase Extensions yang tidak digunakan lagi ke fungsi yang diinstal dan di-deploy pengguna di Cloud Functions mereka sendiri untuk codebase Firebase (generasi ke-2).

Ini adalah jalur migrasi yang direkomendasikan. Firebase akan mengelola daftar ekstensi dengan padanan npm resmi; panduan ini akan memandu Anda membuat ekstensi sendiri.

Di seluruh panduan ini, ekstensi Stream Cloud Firestore to BigQuery (firestore-bigquery-export) digunakan sebagai contoh yang sedang berjalan. Setiap bagian diakhiri dengan Contoh penggunaan yang menunjukkan tampilan ekstensi tersebut sebelum migrasi, dan tampilannya setelah migrasi, sebagai paket @firebase-function-kits/firestore-bigquery-export.

Daftar untuk mendapatkan informasi dan bantuan selengkapnya tentang memigrasikan ekstensi

Jika ada pertanyaan tentang cara bermigrasi dari Firebase Extensions, Anda dapat menghubungi kami di firebase-extensions-migrator-support-external@google.com. Kami juga akan mengirimkan email kepada grup ini saat kami memperbarui panduan dengan informasi selengkapnya tentang pengemasan, pengujian, dan pendistribusian fungsi generasi ke-2 Anda.

Untuk bergabung dengan grup ini, kirim pesan ke firebase-extensions-migrator-support-external+subscribe@google.com, yang akan merespons dengan email permintaan keanggotaan. Anda harus membalas email tersebut, bukan mengklik tombol "Gabung ke Grup Ini".

Sebelum memulai

Untuk menyelesaikan migrasi ini sesuai petunjuk, Anda akan menggunakan fitur Cloud Functions berikut:

  • Konfigurasi Berparameter. Setiap parameter yang Anda deklarasikan di extension.yaml akan menjadi parameter yang ditentukan dalam kode paket Anda.

  • Peran IAM deklaratif dan API yang diperlukan. Setiap peran yang Anda deklarasikan di extension.yaml menjadi panggilan requiresRole(...), dan setiap API menjadi panggilan requiresAPI(...) dalam kode paket Anda. Pada waktu deployment, CLI Firebase memberikan peran yang dideklarasikan ke akun layanan runtime terkelola dan mengaktifkan API yang dideklarasikan atas nama Anda.

  • Peristiwa siklus proses untuk codebase Cloud Functions. Basis kode Cloud Functions kini mendukung peristiwa siklus proses yang serupa dengan Firebase Extensions. Deklarasikan penyiapan waktu penginstalan dan waktu update dengan hook siklus proses afterFirstDeploy(...) dan afterRedeploy(...). Hal ini menggantikan lifecycleEvents yang Anda deklarasikan di extension.yaml.

Memigrasikan sumber Firebase Extensions ke fungsi generasi ke-2

(Opsional) Migrasi otomatis dengan Keahlian Agen Firebase

Anda dapat mengotomatiskan Langkah 1 hingga 8 (menginventarisir resource, memicu upgrade, konversi param dan secret, IAM deklaratif, hook siklus proses, dan membuat README paket) menggunakan keterampilan agen AI extension-to-functions-codebase resmi.

Menginstal keahlian

Jika Anda atau asisten coding AI Anda (Gemini di Firebase, Cursor, Claude Code, GitHub Copilot) belum menginstal keahlian, jalankan perintah berikut menggunakan CLI keahlian:

npx skills add firebase/agent-skills --skill extension-to-functions-codebase

Setelah skill diinstal di project Anda, asisten coding AI Anda akan mengikuti aturan migrasi dan langkah-langkah transformasi secara otomatis. Anda dapat menggunakan perintah berikut:

"Migrasikan Ekstensi Firebase ini ke paket Kit Fungsi Generasi ke-2 yang dapat dipublikasikan dengan mengikuti petunjuk dalam extension-to-functions-codebase skill."

1. Menginventaris ekstensi

Mulailah dengan membuat inventaris ekstensi Anda: daftar lengkap semua hal yang dideklarasikan, dikirim, dan didokumentasikan oleh ekstensi, sehingga setiap perilaku memiliki tujuan yang ditentukan dalam fungsi generasi ke-2 dan tidak ada yang hilang dalam migrasi.

Tinjau setiap hal berikut, dan catat apa yang Anda temukan:

  • extension.yaml, yang mendeklarasikan parameter, fungsi, peristiwa, peran IAM, API yang diperlukan, rahasia, dan hook siklus proses.

  • functions/, yang berisi kode fungsi, dependensi, konfigurasi build, pemicu, dan fungsi task queue.

  • README.md, PREINSTALL.md, dan POSTINSTALL.md, yang berisi langkah-langkah penyiapan, peringatan, dan catatan penagihan.

  • scripts/, yang berisi utilitas impor, pengisian ulang, IAM, perbaikan, atau migrasi, dan alat lainnya yang Anda kirimkan bersama ekstensi.

Kemudian, untuk setiap item di extension.yaml, tentukan lokasi item tersebut dalam paket npm:

  • Konversi konfigurasi pengguna menjadi parameter Cloud Functions (Langkah 4).

  • Konversi secret menjadi secret Cloud Functions (Langkah 4).

  • Konversi peran IAM menjadi deklarasi requiresRole(...) (Langkah 6).

  • Konversi Google API yang diperlukan menjadi deklarasi requiresAPI(...) jika sesuai (Langkah 6).

  • Konversi hook penginstalan dan update menjadi deklarasi afterFirstDeploy(...) dan afterRedeploy(...) (Langkah 7).

  • Mengonversi ID instance dari EXT_INSTANCE_ID ke FIREBASE_KIT_INSTANCE_ID (Langkah 4).

Contoh penggunaan: Streaming Cloud Firestore ke BigQuery

Membaca firestore-bigquery-export/extension.yaml dan functions/ menghasilkan inventaris ini:

Di extension.yaml Jumlah / nilai Tujuan
params 25 (COLLECTION_PATH, DATASET_ID, TABLE_ID, DATASET_LOCATION, VIEW_TYPE, …) Cloud Functions params (Langkah 4)
apis bigquery.googleapis.com requiresAPI(...) (Langkah 6)
roles bigquery.dataEditor, datastore.user, bigquery.user requiresRole(...) (Langkah 6)
resources 1 pemicu peristiwa (fsexportbigquery) + fungsi task queue (initBigQuerySync, setupBigQuerySync) Fungsi paket yang diekspor (Langkah 3)
lifecycleEvents onInstall → initBigQuerySync; onUpdate / onConfigure → setupBigQuerySync afterFirstDeploy / afterRedeploy (Langkah 7)
ID instance Tidak digunakan (tidak ada pembacaan EXT_INSTANCE_ID) Tidak ada yang perlu dimigrasikan
scripts/ import/ (pengisian ulang), gen-schema-view/ Disimpan sebagai skrip (di luar cakupan di sini)

Analisis. Ekstensi tidak mendeklarasikan parameter type: secret, sehingga tidak ada yang perlu dimigrasikan untuk rahasia di Langkah 4. Pemicu peristiwa sudah berupa generasi ke-2; hanya fungsi task queue yang masih berupa generasi ke-1 (relevan di Langkah 3).

2. Perbarui package.json

Perbarui file package.json ekstensi Anda. Jika Anda memigrasikan satu ekstensi, ekstensi ini dapat menjadi package.json root. Jika Anda memigrasikan banyak ekstensi dalam satu repositori, berikan setiap ekstensi paketnya sendiri.

Versi SDK minimum: Deklarasikan firebase-functions >= 7.4.0 dan firebase-admin >= 14.2.0 sebagai dependensi. Deklarasikan versi firebase-functions Anda sebagai dependensi peer juga, sehingga project Cloud Functions pengguna Anda memiliki versi SDK yang sama dengan yang digunakan untuk menulis library Anda.

{
  "name": "<package-name>",
  "version": "1.0.0",
  "main": "lib/index.js",
  "types": "lib/index.d.ts",
  "exports": {
    ".": {
      "types": "./lib/index.d.ts",
      "default": "./lib/index.js"
    }
  },
  "engines": {
    "node": "22"
  },
  "peerDependencies": {
    "firebase-functions": "^7.4.0"
  },
  "dependencies": {
    "firebase-functions": "^7.4.0",
    "firebase-admin": "^14.2.0"
  }
}

Contoh penggunaan: Streaming Cloud Firestore ke BigQuery

Sebelum. functions/package.json ekstensi bersifat pribadi, memberi nama ID ekstensi, dan mendeklarasikan firebase-functions sebagai dependensi langsung:

{
  "name": "firestore-bigquery-export",
  "main": "lib/index.js",
  "private": true,
  "dependencies": {
    "@firebaseextensions/firestore-bigquery-change-tracker": "^2.0.4",
    "firebase-admin": "^14.2.0",
    "firebase-functions": "^6.3.2"
  }
}

Setelah. Paket yang dapat dipublikasikan: nama yang diberi cakupan, peta exports, dan firebase-functions dipindahkan ke peerDependencies:

{
  "name": "@firebase-function-kits/firestore-bigquery-export",
  "version": "0.1.0",
  "main": "lib/index.js",
  "types": "lib/index.d.ts",
  "exports": {
    ".": {
      "types": "./lib/index.d.ts",
      "default": "./lib/index.js"
    }
  },
  "engines": {
    "node": "22"
  },
  "peerDependencies": {
    "firebase-functions": "^7.4.0"
  },
  "dependencies": {
    "@firebaseextensions/firestore-bigquery-change-tracker": "^2.0.4",
    "firebase-admin": "^14.2.0",
    "firebase-functions": "^7.4.0"
  }
}

3. Mengupgrade fungsi dari generasi ke-1 ke generasi ke-2

Jika ekstensi Anda masih mengekspor fungsi generasi ke-1, konversi setiap pemicu ke pemicu generasi ke-2 yang setara. Impor dari modul firebase-functions/..., dan teruskan setelan runtime di opsi pemicu.

Lihat panduan upgrade generasi ke-2 Cloud Functions. Khususnya, Anda dapat meminimalkan upaya penulisan ulang dengan menggunakan penghancuran struktur peristiwa yang di-patch generasi ke-2 dan menghindari penulisan ulang logika fungsi karena SDK generasi ke-2 mengekspos parameter v1 sebagai kolom dalam objek peristiwa, sehingga Anda dapat menggunakan parameter yang dihancurkan/dinamai dan mempertahankan logika bisnis Anda tanpa perubahan.

Sebelum. Generasi ke-1:

import * as functions from "firebase-functions/v1";

export const sync = functions.firestore
  .document("{collectionId}/{documentId}")
  .onWrite(async (change, context) => {
    await handleWrite(change.before, change.after, context.params);
  });

Setelah. Generasi ke-2:

import { onDocumentWritten } from "firebase-functions/firestore";

export const syncV2 = onDocumentWritten(
  { document: "{collectionId}/{documentId}" },
  async ({ change, context }) =>
    await handleWrite(change.before, change.after, context.params)
);

Lihat perbandingan versi Cloud Functions untuk mengetahui daftar lengkap perbedaan antara fungsi generasi ke-1 dan generasi ke-2.

4. Mengonversi parameter dan secret ekstensi

Params

Setiap parameter yang Anda deklarasikan di extension.yaml akan menjadi parameter Cloud Functions.

Mengonversi pembacaan lingkungan langsung:

const collectionPath = process.env.COLLECTION_PATH;

ke dalam parameter Cloud Functions:

import { defineString } from "firebase-functions/params";
import { onDocumentWritten } from "firebase-functions/firestore";

const collectionPath = defineString("COLLECTION_PATH");

// Pass the param directly when used as a placeholder (e.g. trigger path)
export const sync = onDocumentWritten(
  { document: collectionPath },
  async (event) => {
    // Call .value() to read the string inside a handler
    const path = collectionPath.value();
    await handleWrite(path, event);
  }
);

Gunakan collectionPath.value() untuk membaca string di dalam handler; gunakan collectionPath secara langsung di tempat placeholder diharapkan, seperti jalur pemicu fungsi.

CLI Firebase menemukan parameter Anda dan membaca nilainya dari .env, .env.<projectId>, atau meminta pengguna Anda selama deployment. Pertahankan nama parameter yang sama, sehingga nilai dari penginstalan yang ada akan dipertahankan.

Anda tidak boleh mengubah nama parameter yang dideklarasikan dalam kode Anda sama sekali. Migrasi ekstensi mempertahankan nilai parameter pengguna akhir yang ada secara otomatis, tetapi hanya jika namanya tidak berubah.

Contoh penggunaan: Streaming Cloud Firestore ke BigQuery

Sebelum. Parameter yang dideklarasikan di extension.yaml, dibaca sebagai variabel lingkungan mentah di config.ts:

# extension.yaml
- param: COLLECTION_PATH
  label: Collection path
  type: string
  required: true
// functions/src/config.ts
collectionPath: process.env.COLLECTION_PATH,

Setelah. Satu defineString; CLI menemukannya dan membaca dari .env:

// src/config.ts
import { defineString } from "firebase-functions/params";

collectionPath: defineString("COLLECTION_PATH", {
  label: "Collection path",
  // We now support "nonEmpty: true" to ensure a value other than the empty string
  // is entered, analogous to "required: true" in extension.yaml
  input: { text: { nonEmpty: true } }
}),

Nama parameter tidak berubah, sehingga .env yang ada tetap berfungsi.

ID instance

Ekstensi membaca ID instance-nya dari EXT_INSTANCE_ID, yang disuntikkan oleh runtime Ekstensi. Kit fungsi membaca ID instance-nya dari FIREBASE_KIT_INSTANCE_ID, yang ditetapkan oleh CLI Firebase untuk setiap instance kit ke kunci instance dalam peta instances di firebase.json. CLI menyediakannya selama penemuan waktu deployment, di emulator, dan ke fungsi yang di-deploy.

ID instance bukan merupakan parameter, jadi jangan deklarasikan dengan defineString. Sebenarnya, FIREBASE_... adalah awalan yang dicadangkan dalam file .env, sehingga pengguna tidak akan dapat menyetel atau menggantinya di sana. Nilai yang dimasukkan CLI tidak terlihat oleh sistem parameter. Membacanya langsung dari lingkungan:

// Before
const instanceId = process.env.EXT_INSTANCE_ID;

// After
const instanceId = process.env.FIREBASE_KIT_INSTANCE_ID;

Variabel hanya akan ditetapkan saat paket Anda di-deploy sebagai kit. Jika kode Anda juga di-deploy sebagai codebase mandiri (lihat Langkah 9), perlakukan kode tersebut sebagai opsional atau gagal dengan cepat dengan pesan yang jelas jika kode tersebut tidak ada. Jika ekstensi Anda mengekspos ID instance sebagai parameter yang ditampilkan kepada pengguna, Anda harus menghapus parameter tersebut karena CLI Firebase kini memiliki nilai tersebut.

Contoh penggunaan: Menghapus Data Pengguna

(Ekstensi Cloud Firestore ke BigQuery tidak membaca ID instance-nya, jadi tidak ada yang perlu dimigrasikan di sana. Ekstensi Hapus Data Pengguna menggunakannya untuk memberi nama topik Pub/Sub.)

Sebelum. Dibaca sebagai variabel lingkungan mentah di config.ts dengan awalan ext- yang digunakan Ekstensi untuk sumber dayanya:

// functions/src/config.ts
discoveryTopic: `ext-${process.env.EXT_INSTANCE_ID}-discovery`,
deletionTopic: `ext-${process.env.EXT_INSTANCE_ID}-deletion`,

Setelah. Pembacaan process.env biasa dari FIREBASE_KIT_INSTANCE_ID yang digunakan untuk dua parameter biasa sehingga pengguna dapat mengganti nama topik:

// src/config.ts
const instanceId = process.env.FIREBASE_KIT_INSTANCE_ID;

// Non-empty defaults so Pub/Sub trigger bindings resolve during deploy
// discovery without freezing an empty topic name into the manifest.
discoveryTopicName: defineString("DISCOVERY_TOPIC_NAME", {
  default: `kit-${instanceId}-discovery`,
}),
deletionTopicName: defineString("DELETION_TOPIC_NAME", {
  default: `kit-${instanceId}-deletion`,
}),

Nilai default tidak boleh kosong karena binding pemicu diselesaikan pada waktu penemuan. Default kosong akan ditulis ke manifes deployment sebagai nama topik. Kit ini juga defensif terhadap eksekusi di luar konteks kit. Jika variabel tidak ada, default tingkat modul akan dievaluasi menjadi kit-undefined-discovery, sehingga pemuat konfigurasi gagal dengan error yang menjelaskan sebagai gantinya:

// ...
const instanceId = process.env.FIREBASE_KIT_INSTANCE_ID;
if (!instanceId) {
  throw new Error(
    "FIREBASE_KIT_INSTANCE_ID is not set. It is provided automatically to " +
      "kit instances by firebase-tools >= 15.32.0; deploy or emulate this " +
      "kit with a supported CLI version."
  );
}
// ...

Pemeriksaan ini berjalan saat pengendali pertama kali menyelesaikan konfigurasinya, sehingga variabel yang tidak ada akan menghasilkan error runtime yang jelas, bukan fungsi yang terikat secara diam-diam ke topik kit-undefined-*. Untuk menolak deployment itu sendiri, lakukan pemeriksaan di cakupan modul sehingga berjalan selama penemuan. Karena CLI mendapatkan ID instance dari firebase.json, tidak ada INSTANCE_ID yang dapat dikonfigurasi, dan tidak ada yang perlu disinkronkan di beberapa instance.

Rahasia

Di extension.yaml, Anda mendeklarasikan secret dengan type: secret. Runtime Ekstensi menyimpan dan mengikatnya, sehingga kode ekstensi Anda dapat membaca process.env.PARAM_NAME secara langsung. Dalam codebase Cloud Functions yang umum, Anda mendeklarasikan dan mengikat setiap rahasia secara eksplisit:

import { defineSecret } from "firebase-functions/params";
import { onRequest } from "firebase-functions/https";

const apiKey = defineSecret("API_KEY");
export const fn = onRequest({ secrets: [apiKey] }, handler);

Setelah ekstensi dimigrasikan ke paket/kit npm, referensi rahasia dikelola dalam file .env pengguna akhir. Anda tidak boleh mengubah nama rahasia yang dideklarasikan dalam kode Anda sama sekali. Selama migrasi, secret pengguna akhir dimigrasikan sebagaimana mestinya.

Contoh penggunaan: Memicu Email Dari Cloud Firestore

Sebelum. MAIL_COLLECTION dan SMTP_PASSWORD dibaca sebagai variabel lingkungan mentah di config.ts:

# extension.yaml
- param: MAIL_COLLECTION
  label: Email documents collection
  type: string
  default: mail
  required: true

- param: SMTP_PASSWORD
  label: SMTP password
  type: secret
// functions/src/config.ts
mailCollection: process.env.MAIL_COLLECTION,
smtpPassword: process.env.SMTP_PASSWORD,

Setelah. Satu defineString dan satu defineSecret; CLI menemukan keduanya dan membaca dari .env:

import { defineString, defineSecret } from "firebase-functions/params";
import { onDocumentWritten } from "firebase-functions/firestore";

const mailCollection = defineString("MAIL_COLLECTION", {
  label: "Email documents collection",
  default: "mail"
});

const smtpPassword = defineSecret("SMTP_PASSWORD", { label: "SMTP password" });

export const processQueue = onDocumentWritten(
  { document: `${mailCollection}/{documentId}`, secrets: [smtpPassword] },
  async (event) => {
    const collection = mailCollection.value();
    const password = smtpPassword.value();
    // ...
  }
);

5. Memigrasikan panggilan task queue internal

Beberapa ekstensi mengantrekan tugas ke antrean tugas sendiri dari dalam kode fungsi, menggunakan Firebase Admin SDK. Hal ini berbeda dengan menerima tugas yang dikirim (dibahas di bagian Mengupgrade fungsi dan Mengonversi hook siklus proses). Di sini, kode Anda adalah produser yang memanggil queue.enqueue(...).

Versi Admin SDK sebelumnya mengharuskan ekstensi meneruskan ID instance ekstensi mereka sendiri sebagai parameter kedua untuk menargetkan fungsi Task Queue di ekstensi yang sama. Mulai firebase-admin 14.2.0, hal ini tidak diperlukan dan tidak direkomendasikan. Task Queue API kini menargetkan antrean tugas dalam konteks yang sama (misalnya, ekstensi atau kit) secara default. Parameter ini aman dan sebaiknya dihapus dalam kode Anda, baik sebagai ekstensi maupun sebagai fungsi mandiri. Menghapus parameter ini memastikan portabilitas dan kompatibilitas ke depan.

Semua hal lainnya tentang panggilan antrean — jalur resource locations/<region>/functions/<name>, payload tugas, dan logika percobaan ulang — tetap sama.

Baca artikel Mengantrekan fungsi dengan Cloud Tasks untuk mengetahui detail selengkapnya tentang cara mengantrekan fungsi dengan Cloud Tasks.

Sebelum. Ekstensi generasi ke-1:

import { getFunctions } from "firebase-admin/functions";

const queue = getFunctions().taskQueue(
  `locations/${config.location}/functions/syncBigQuery`,
  process.env.EXT_INSTANCE_ID, // extension instance ID, injected by the runtime
);
await queue.enqueue(taskData);

Setelah. Ekstensi generasi ke-2:

import { getFunctions } from "firebase-admin/functions";

const queue = getFunctions().taskQueue(
  `locations/${process.env.FUNCTION_REGION}/functions/syncBigQuery`
);
await queue.enqueue(taskData);

Jika panggilan antrean Anda menargetkan codebase yang diberi awalan, nama fungsi yang ditemukan juga diberi awalan (misalnya, orders-syncBigQuery); lihat Meninjau dan menginstal instance pengganti kit fungsi dan Menguji sebagai kit fungsi.

6. Mendeklarasikan API dan peran IAM yang diperlukan

Pindahkan persyaratan IAM dan API ekstensi Anda dari extension.yaml ke dalam kode:

import { requiresAPI, requiresRole } from "firebase-functions";

requiresAPI("bigquery.googleapis.com", "Needed to write changelog rows");
requiresRole("roles/bigquery.dataEditor");
requiresRole("roles/bigquery.user");

Dengan keamanan deklaratif, CLI Firebase membuat atau memperbarui akun layanan runtime terkelola untuk codebase, dan memberikan gabungan semua peran yang dideklarasikan. Dokumentasikan untuk pengguna Anda bahwa semua fungsi dalam codebase berjalan dengan peran tersebut, kecuali jika API akhir mendukung model yang lebih sempit.

Contoh penggunaan: Streaming Cloud Firestore ke BigQuery

Sebelum. Dideklarasikan di extension.yaml; runtime Ekstensi mengaktifkan API dan memberikan peran ke akun terkelola:

apis:
  - apiName: bigquery.googleapis.com
roles:
  - role: bigquery.dataEditor
  - role: datastore.user
  - role: bigquery.user

Setelah. Dideklarasikan dalam kode dengan requiresAPI dan requiresRole:

import { requiresAPI, requiresRole } from "firebase-functions";

requiresAPI(
  "bigquery.googleapis.com",
  "Needed to write changelog rows and views"
);
requiresRole("roles/bigquery.dataEditor");
requiresRole("roles/datastore.user");
requiresRole("roles/bigquery.user");

7. Mengonversi hook siklus proses

Jika ekstensi Anda memanggil getExtensions().runtime() (misalnya, setProcessingState atau setFatalError), hapus panggilan tersebut karena akan menampilkan error jika dipanggil dari fungsi generasi ke-2 yang di-deploy secara normal. Status siklus proses kini didorong oleh afterFirstDeploy dan afterRedeploy, tempat pelacakan status ini tidak digunakan.

Firebase Extensions dapat menjalankan penyiapan saat pengguna menginstal, mengupdate, atau mengonfigurasi ulang ekstensi. Dalam paket npm, deklarasikan tindakan siklus proses yang setara dalam kode.

Untuk penyiapan satu kali:

import { afterFirstDeploy } from "firebase-functions/lifecycle";
import { onTaskDispatched } from "firebase-functions/tasks";

export const runInitialSetup = onTaskDispatched(async (request) => {
  await initializeResources(request.data);
});

afterFirstDeploy({
  task: {
    function: "runInitialSetup",
    body: {}
  }
});

Untuk pembaruan konfigurasi atau kode:

import { afterRedeploy } from "firebase-functions/lifecycle";

afterRedeploy({
  task: {
    function: "runInitialSetup",
    body: { reconcile: true }
  }
});

Buat tindakan siklus proses Anda bersifat idempoten. Pengguna Anda mungkin perlu menjalankan ulang secara manual jika pengiriman atau eksekusi gagal:

firebase functions:lifecycle:run afterFirstDeploy CODEBASE_NAME
firebase functions:lifecycle:run afterRedeploy CODEBASE_NAME

Contoh penggunaan: Streaming Cloud Firestore ke BigQuery

Sebelum. lifecycleEvents di extension.yaml, yang didorong oleh runtime Ekstensi:

lifecycleEvents:
  onInstall:
    function: initBigQuerySync
    processingMessage: Configuring BigQuery Sync.
  onUpdate:
    function: setupBigQuerySync
    processingMessage: Configuring BigQuery Sync
  onConfigure:
    function: setupBigQuerySync
    processingMessage: Configuring BigQuery Sync

Setelah. Dideklarasikan dalam kode; tugas menyediakan BigQuery pada deployment pertama:

import { afterFirstDeploy, afterRedeploy } from "firebase-functions/lifecycle";

afterFirstDeploy({ task: { function: "initBigQuerySync" } });
afterRedeploy({ task: { function: "setupBigQuerySync" } });

Penyediaan bersifat idempoten, sehingga penayangan ulang akan merekonsiliasi set data, tabel, dan tampilan. Pengguna dapat menjalankan ulang secara manual dengan firebase functions:lifecycle:run afterFirstDeploy CODEBASE_NAME.

8. Menyiapkan dokumen untuk pengguna Anda

Tulis paket README yang menjelaskan, setidaknya:

  • Nilai .env yang diperlukan paket.
  • Secret yang diperlukan paket, dan cara memigrasikan nilai secret yang ada.
  • Peran IAM yang dideklarasikan paket dengan requiresRole(...).
  • Google API yang diaktifkan atau diperlukan oleh paket.
  • Hook siklus proses yang dideklarasikan paket, dan cara menjalankannya ulang secara manual.
  • Catatan penagihan.
  • Apa yang berubah dibandingkan dengan ekstensi asli.
  • Cara paket mendapatkan ID instance-nya (FIREBASE_KIT_INSTANCE_ID yang ditetapkan oleh CLI) dan semua fungsi instance di-deploy dengan awalan kit-<instanceId>-.

Contoh penggunaan: Streaming Cloud Firestore ke BigQuery

Paket README mengirimkan tabel "apa yang berubah" yang konkret:

Kepedulian Sebagai ekstensi Sebagai @firebase-function-kits/firestore-bigquery-export
Konfigurasi Parameter ekstensi Cloud Functions params melalui .env
IAM Diberikan oleh Ekstensi requiresRole(...), diterapkan saat deployment
Penyediaan Tugas siklus proses menurut Ekstensi afterFirstDeploy / afterRedeploy tugas
Nama fungsi ext-<instanceId>-fsexportbigquery fsexportbigquery (dapat diawali)
ID instance EXT_INSTANCE_ID yang dimasukkan oleh Ekstensi FIREBASE_KIT_INSTANCE_ID, ditetapkan oleh CLI dari firebase.json

9. Menguji fungsi generasi ke-2

Sekarang Anda akan memiliki fungsi generasi ke-2 yang, saat di-deploy, berperilaku sama dengan penginstalan baru ekstensi Anda. Langkah berikutnya adalah memverifikasi dan memperbaiki masalah yang tidak sengaja muncul selama proses tersebut.

Untuk memanggil setGlobalOptions guna menyetel opsi global seperti region atau CPU default, Anda hanya boleh melakukannya saat men-deploy kit sebagai fungsi generasi ke-2 mandiri. Saat diinstal sebagai paket npm, pengguna Anda akan memanggil setGlobalOptions dalam kode pembungkusnya untuk mengonfigurasi parameter ini, dan mereka akan mendapatkan peringatan jika hal ini terjadi dua kali. Anda dapat melindungi panggilan ini dengan memeriksa variabel lingkungan FIREBASE_KIT_INSTANCE_ID:

import { setGlobalOptions } from "firebase-functions";

if (!process.env.FIREBASE_KIT_INSTANCE_ID) {
  setGlobalOptions({
    region: "us-east1",
    maxInstances: 10,
  });
}

Pastikan Anda menggunakan firebase-tools >= 15.32.0, dan men-deploy fungsi generasi ke-2 yang dikonversi ke project pengujian dengan resource yang sesuai untuk menguji perilakunya. Jika Anda sudah menyiapkan project pengujian dari pengujian ekstensi, jalankan perintah berikut:

firebase deploy --only functions

Isi wizard yang muncul dan meminta nilai parameter dengan cara yang sama seperti Anda mengisi formulir penginstalan di konsol Firebase untuk ekstensi.

Contoh penggunaan: Streaming Cloud Firestore ke BigQuery

Kami memverifikasi sinkronisasi Cloud Firestore ke BigQuery secara end-to-end:

  1. Di halaman Cloud Firestore pada konsol Firebase, buat koleksi yang Anda tetapkan sebagai COLLECTION_PATH (users) jika belum ada.
  2. Buat dokumen bernama bigquery-mirror-test yang berisi kolom apa pun dengan nilai apa pun.
  3. Di halaman BigQuery pada konsol Google Cloud, kueri tabel log perubahan mentah. Tabel ini harus berisi satu baris yang mencatat pembuatan dokumen:

    SELECT * FROM `PROJECT_ID.analytics.users_raw_changelog`
    
  4. Kueri tampilan terbaru, yang akan menampilkan peristiwa perubahan terbaru untuk satu-satunya dokumen yang ada (bigquery-mirror-test):

    SELECT * FROM `PROJECT_ID.analytics.users_raw_latest`
    
  5. Hapus dokumen bigquery-mirror-test di Cloud Firestore. Perubahan ini akan hilang dari tampilan terbaru, dan peristiwa DELETE ditambahkan ke tabel log perubahan mentah.

    Anda dapat memeriksa histori lengkap satu dokumen dengan:

    SELECT *
       FROM `PROJECT_ID.analytics.users_raw_changelog`
       WHERE document_name = "bigquery-mirror-test"
       ORDER BY timestamp ASC
    

Perbedaan dari pengujian ekstensi:

  • Pemicu di-deploy sebagai fsexportbigquery (tanpa awalan saat di-deploy sebagai fungsi generasi ke-2 biasa, bukan kit), bukan ext-<instanceId>-fsexportbigquery. Cari nama tersebut di dasbor dan log Cloud Functions.
  • Kode Anda kini berjalan di Firebase Local Emulator Suite sebagai fungsi normal. Anda dapat menetapkan nilai parameter yang akan digunakan di emulator dengan .env.local. Anda juga dapat melakukan pengujian unit pada kode menggunakan SDK firebase-functions-test seperti yang dijelaskan dalam Pengujian unit Cloud Functions.
  • Penyediaan tidak lagi didorong oleh runtime Ekstensi. Jika tabel log perubahan tidak ada setelah deployment, jalankan ulang tugas penyiapan secara manual: firebase functions:lifecycle:run afterFirstDeploy CODEBASE_NAME. Tugas ini bersifat idempoten, sehingga menjalankan ulang tugas ini akan merekonsiliasi set data, tabel, dan tampilan.
  • Nilai parameter berasal dari .env, bukan formulir penginstalan, sehingga firebase deploy yang dijalankan ulang tidak interaktif setelah .env selesai.

10. Memublikasikan kit fungsi Anda di npm

Setelah memvalidasi konversi dari ekstensi ke fungsi generasi ke-2, Anda dapat memublikasikan kandidat rilis ke npm untuk pengujian end-to-end menggunakan salah satu panduan berikut:

Setelah dipublikasikan ke npm, kit Anda dapat diinstal dengan firebase functions:kits:install dan dicantumkan sebagai pengganti resmi untuk ekstensi Anda.

Sebaiknya publikasikan kandidat rilis terlebih dahulu. Kit diinstal berdasarkan nama paket dan versi, sehingga pra-rilis memungkinkan Anda menguji alur penginstalan sebenarnya terhadap registry tanpa mengekspos paket yang belum selesai kepada pengguna yang menginstal dengan tag latest default.

Sebelum Anda memublikasikan:

  1. Pilih nama paket. Nama yang tercakup dan tidak tercakup berfungsi (lihat panduan di atas). Perhatikan bahwa paket yang memiliki cakupan bersifat pribadi secara default, jadi teruskan --access public.
  2. Bangun dan periksa apa yang dikirim. main dan types mengarah ke output yang dikompilasi (lib/ dalam contoh yang kami kerjakan), sehingga direktori tersebut harus disertakan dalam file tar yang dipublikasikan. Gunakan .npmignore atau daftar yang diizinkan files, dan periksa hasilnya dengan npm pack --dry-run. Skrip prepublishOnly yang menjalankan build Anda mencegah publikasi output yang tidak berlaku.

Penambahan package.json yang membuat publikasi aman secara default:

{
  "files": ["lib", "README.md", "CHANGELOG.md"],
  "publishConfig": { "access": "public", "tag": "next" },
  "scripts": {
    "build": "tsc -b",
    "prepublishOnly": "npm run build && npm test"
  }
}

Kolom "publishConfig": { "tag": "next" } memastikan bahwa npm publish biasa tidak pernah menggantikan latest.

Membuat kandidat rilis:

Misalnya, untuk menaikkan versi secara lokal dari 0.0.2-rc.3 ke 0.0.2-rc.4 (ini melakukan commit dan memberi tag di Git jika package.json berada di root repositori):

npm version prerelease --preid rc

Untuk memublikasikan kandidat rilis dan mendaftarkan @your-org/your-kit@0.0.2-rc.4 di npm dengan tag next:

npm publish

Situs npm mungkin memerlukan waktu beberapa menit untuk menampilkan versi baru; npm view membaca registry secara langsung:

npm view @your-org/your-kit versions dist-tags

Setelah Langkah 11 dan Langkah 12 dalam panduan ini selesai, Anda dapat mempromosikan paket ke versi stabil:

npm version 0.0.2
npm publish --tag latest
npm dist-tag add @your-org/your-kit@0.0.2 next

Catatan tentang npm-shrinkwrap.json: Sebaiknya sertakan file npm-shrinkwrap.json dalam paket Anda; CLI akan memperingatkan pengguna saat penginstalan jika Anda tidak melakukannya. Hal ini memastikan pengguna menggunakan dependensi persis yang Anda uji dan membantu melindungi dari serangan supply chain. Namun, shrinkwrap diterapkan kata demi kata dalam project pengguna Anda, termasuk selama build Cloud Functions (npm ci), tempat entri khusus developer dapat gagal dengan EBADPLATFORM. Anda mungkin perlu menghapus entri "dev": true dan devDependencies dari salinan shrinkwrap yang dipublikasikan.

Contoh penggunaan: Streaming Cloud Firestore ke BigQuery

package.json kit pada saat kandidat rilis keempat:

{
  "name": "@firebase-function-kits/firestore-bigquery-export",
  "version": "0.0.2-rc.4",
  "repository": {
    "type": "git",
    "url": "https://github.com/firebase/extensions.git",
    "directory": "kits/firestore-bigquery-export"
  },
  "main": "lib/index.js",
  "types": "lib/index.d.ts",
  "engines": { "node": "22" },
  "scripts": { "build": "tsc -b" }
}

Perhatikan bahwa dalam kasus ini, kit berada di monorepo, jadi penting untuk menyertakan repository.directory untuk link registry npm agar mengarah ke folder yang benar. CHANGELOG.md-nya menyimpan catatan untuk rilis yang tertunda.

11. Pengujian sebagai kit fungsi

Setelah memublikasikan kit, sebaiknya uji kit menggunakan npm.

Pastikan Anda menggunakan firebase-tools versi >= 15.32.0 dan instal kit:

firebase functions:kits:install --package <your-package-name>@<your-prerelease-version>

Tindakan ini akan mendownload paket Anda dari npm, menyiapkannya di dalam direktori sumber baru untuk kit Anda, dan memandu Anda mengonfigurasi instance pertama yang serupa dengan alur penginstalan ekstensi. Setelah menginstal dan menyiapkan paket secara lokal, jalankan deployment untuk membuat resource di project Google Cloud Anda:

firebase deploy --only functions:<your-kit-instance-id>

Setelah penginstalan, CLI Firebase akan mencetak perintah deployment serupa dengan ID instance persis yang Anda pilih selama penginstalan.

Validasi kembali kit Anda menggunakan petunjuk di Langkah 9. Uji fungsi generasi ke-2 Anda. Sekarang setelah Anda men-deploy menggunakan kit, fungsi Anda akan diberi awalan dan nama kit-<instance-id>-<method-name>. Hal ini memungkinkan kit memiliki beberapa instance, men-deploy fungsi yang sama beberapa kali dalam project, masing-masing dengan nama yang unik.

12. Penggantian pengujian migrasi

Anda dapat menyiapkan instance ekstensi yang berfungsi, lalu menggunakan panduan migrasi pengguna (menggunakan firebase ext:migrate --package atau perintah CLI kit fungsi) untuk menyelesaikan pengujian kit fungsi sebagai pengganti migrasi.

13. Memberi tahu pengguna dan Google tentang penggantian ekstensi resmi Anda

Setelah penggantian kit fungsi Anda siap dan tersedia sebagai paket npm yang harus dimigrasikan oleh pengguna, beri tahu pengguna dan Google tentang penggantian resmi ini. Perbarui file README.md di repositori GitHub yang menghosting ekstensi Anda dengan informasi berikut:

<!-- FIREBASE_EXTENSION_REPLACEMENT: extension="<your-extesion-id>" package="<your-npm-package-name>" -->
> [!WARNING]
> **Deprecation Notice:** The Firebase Extension `<your-extension>` is deprecated. Migrate to the [<your-npm-package-name>](<link-to-your-npm-package>) package.

Google memindai README ekstensi yang diketahui untuk menemukan komentar seperti <!-- FIREBASE_EXTENSION_REPLACEMENT: extension="firebase/firestore-bigquery-export" package="@firebase-function-kits/firestore-bigquery-export" --> dan menggunakan komentar ini untuk mengisi registry resmi penggantian yang disimpan di repositori firebase-tools sebagai replacements.json. Anda juga dapat memeriksa replacements.json untuk melihat README.md mana yang akan dipindai untuk ekstensi Anda. Daftar resmi penggantian diperbarui setiap minggu.

Contoh penggunaan: Streaming Cloud Firestore ke BigQuery

Ekstensi firestore-bigquery-export README.md berisi:

<!-- FIREBASE_EXTENSION_REPLACEMENT: extension="firebase/firestore-bigquery-export" package="@firebase-function-kits/firestore-bigquery-export" -->
> [!WARNING]
> **Deprecation Notice:** The Firebase Extension `firebase/firestore-bigquery-export` is deprecated. Please migrate to the [`@firebase-function-kits/firestore-bigquery-export`](https://www.npmjs.com/package/@firebase-function-kits/firestore-bigquery-export) package.