Hướng dẫn này cho bạn biết cách di chuyển các tiện ích của bạn từ môi trường Firebase Extensions ngừng hoạt động sang một hàm mà người dùng cài đặt và triển khai trong Cloud Functions của riêng họ cho toàn bộ mã nguồn Firebase (thế hệ thứ 2).
Đây là phương pháp di chuyển được đề xuất. Firebase sẽ duy trì danh sách các tiện ích có phiên bản tương đương chính thức trên npm; hướng dẫn này sẽ hướng dẫn bạn cách tạo tiện ích của riêng mình.
Trong hướng dẫn này, tiện ích Stream Cloud Firestore to BigQuery (firestore-bigquery-export) được dùng làm ví dụ minh hoạ. Mỗi phần kết thúc bằng một Ví dụ đã thực hiện cho thấy tiện ích đó trông như thế nào trước khi di chuyển và trông như thế nào sau khi di chuyển, dưới dạng gói @firebase-function-kits/firestore-bigquery-export.
Đăng ký để nhận thêm thông tin và được trợ giúp về việc di chuyển tiện ích
Nếu có thắc mắc về cách di chuyển từ Firebase Extensions, bạn có thể liên hệ với chúng tôi tại firebase-extensions-migrator-support-external@google.com. Chúng tôi cũng sẽ gửi email cho nhóm này khi cập nhật hướng dẫn để cung cấp thêm thông tin về việc đóng gói, kiểm thử và phân phối các hàm thế hệ thứ 2.
Để tham gia nhóm này, hãy gửi tin nhắn đến firebase-extensions-migrator-support-external+subscribe@google.com. Nhóm sẽ phản hồi bằng một email yêu cầu tham gia. Bạn phải trả lời email đó, chứ không được nhấp vào nút "Tham gia nhóm này".
Trước khi bắt đầu
Để hoàn tất quá trình di chuyển này như đã viết, bạn sẽ sử dụng các tính năng sau của Cloud Functions:
Cấu hình được tham số hoá. Mỗi tham số mà bạn khai báo trong
extension.yamlsẽ trở thành một tham số được xác định trong mã gói của bạn.Các vai trò IAM khai báo và API bắt buộc. Mỗi vai trò mà bạn khai báo trong
extension.yamlsẽ trở thành một lệnh gọirequiresRole(...)và mỗi API sẽ trở thành một lệnh gọirequiresAPI(...)trong mã gói của bạn. Tại thời điểm triển khai, CLI Firebase sẽ cấp các vai trò đã khai báo cho tài khoản dịch vụ thời gian chạy được quản lý và thay mặt bạn bật các API đã khai báo.Sự kiện trong vòng đời cho cơ sở mã Cloud Functions. Giờ đây, cơ sở mã Cloud Functions hỗ trợ các sự kiện trong vòng đời tương tự như Firebase Extensions. Khai báo chế độ thiết lập khi cài đặt và khi cập nhật bằng các lệnh gọi vòng đời
afterFirstDeploy(...)vàafterRedeploy(...). Các phương thức này thay thếlifecycleEventsmà bạn khai báo trongextension.yaml.
Di chuyển nguồn Firebase Extensions sang hàm thế hệ thứ 2
(Không bắt buộc) Di chuyển tự động bằng Kỹ năng Firebase của tác nhân
Bạn có thể tự động hoá Các bước từ 1 đến 8 (kiểm kê tài nguyên, nâng cấp trình kích hoạt, chuyển đổi tham số và bí mật, IAM khai báo, các lệnh gọi lại vòng đời và tạo tệp README của gói) bằng cách sử dụng kỹ năng chính thức của tác nhân AI extension-to-functions-codebase.
Cài đặt kỹ năng
Nếu bạn hoặc trợ lý AI lập trình (Gemini trong Firebase, Cursor, Claude Code, GitHub Copilot) chưa cài đặt kỹ năng này, hãy chạy lệnh sau bằng CLI của kỹ năng:
npx skills add firebase/agent-skills --skill extension-to-functions-codebase
Sau khi kỹ năng này được cài đặt trong dự án của bạn, trợ lý lập trình AI sẽ tự động tuân theo các quy tắc di chuyển và bước chuyển đổi của kỹ năng đó. Bạn có thể sử dụng câu lệnh sau:
"Vui lòng di chuyển Tiện ích Firebase này vào một gói Function Kit thế hệ thứ 2 có thể xuất bản theo hướng dẫn trong kỹ năng extension-to-functions-codebase."
1. Kiểm kê tiện ích
Bắt đầu bằng cách lập danh mục tiện ích của bạn: một danh sách đầy đủ về mọi thứ mà tiện ích khai báo, vận chuyển và ghi lại, để mọi hành vi đều có một đích đến được xác định trong hàm thế hệ thứ 2 và không có gì bị mất trong quá trình di chuyển.
Hãy xem xét từng mục sau đây và ghi lại những gì bạn thấy:
extension.yaml, khai báo các tham số, hàm, sự kiện, vai trò IAM, API bắt buộc, khoá bí mật và lệnh gọi vòng đời.functions/, chứa mã hàm, các phần phụ thuộc, cấu hình bản dựng, điều kiện kích hoạt và các hàm hàng đợi tác vụ.README.md,PREINSTALL.mdvàPOSTINSTALL.md, trong đó có các bước thiết lập, cảnh báo và ghi chú về việc thanh toán.scripts/, chứa mọi tiện ích nhập, bổ sung dữ liệu, IAM, sửa chữa hoặc di chuyển, cũng như mọi công cụ khác mà bạn cung cấp cùng với tiện ích.
Sau đó, đối với mỗi mục trong extension.yaml, hãy quyết định vị trí của mục đó trong gói npm:
Chuyển đổi cấu hình người dùng thành các thông số Cloud Functions (Bước 4).
Chuyển đổi các khoá bí mật thành các khoá bí mật Cloud Functions (Bước 4).
Chuyển đổi các vai trò IAM thành khai báo
requiresRole(...)(Bước 6).Chuyển đổi các API bắt buộc của Google thành các khai báo
requiresAPI(...)khi thích hợp (Bước 6).Chuyển đổi các lệnh gọi cài đặt và cập nhật thành các khai báo
afterFirstDeploy(...)vàafterRedeploy(...)(Bước 7).Chuyển đổi mã nhận dạng phiên bản từ
EXT_INSTANCE_IDthànhFIREBASE_KIT_INSTANCE_ID(Bước 4).
Ví dụ minh hoạ: Truyền phát Cloud Firestore đến BigQuery
Đọc firestore-bigquery-export/extension.yaml và functions/ sẽ tạo ra khoảng không quảng cáo sau:
Trong extension.yaml |
Số lượng / giá trị | Nơi dữ liệu được chuyển đến |
|---|---|---|
params |
25 (COLLECTION_PATH, DATASET_ID, TABLE_ID, DATASET_LOCATION, VIEW_TYPE, …) |
Cloud Functions params (Bước 4) |
apis |
bigquery.googleapis.com |
requiresAPI(...) (Bước 6) |
roles |
bigquery.dataEditor, datastore.user, bigquery.user |
requiresRole(...) (Bước 6) |
resources |
1 điều kiện kích hoạt sự kiện (fsexportbigquery) + các hàm hàng đợi tác vụ (initBigQuerySync, setupBigQuerySync) |
Hàm gói được xuất (Bước 3) |
lifecycleEvents |
onInstall → initBigQuerySync; onUpdate / onConfigure → setupBigQuerySync |
afterFirstDeploy / afterRedeploy (Bước 7) |
| Mã phiên bản | Không dùng (không có EXT_INSTANCE_ID nào) |
Không có dữ liệu nào để di chuyển |
scripts/ |
import/ (backfill), gen-schema-view/ |
Được giữ lại dưới dạng tập lệnh (ngoài phạm vi ở đây) |
Phân tích. Tiện ích không khai báo tham số type: secret nào, nên không có gì để di chuyển cho các khoá bí mật trong Bước 4. Trình kích hoạt sự kiện đã là thế hệ thứ 2; chỉ có các hàm hàng đợi tác vụ vẫn là thế hệ thứ 1 (có liên quan trong Bước 3).
2. Cập nhật package.json
Cập nhật tệp package.json của tiện ích. Nếu bạn đang di chuyển một tiện ích, thì đây có thể là package.json gốc. Nếu bạn đang di chuyển nhiều tiện ích trong một kho lưu trữ, hãy cung cấp cho mỗi tiện ích một gói riêng.
Phiên bản SDK tối thiểu: Khai báo firebase-functions >=
7.4.0 và firebase-admin >=
14.2.0 làm phần phụ thuộc. Khai báo phiên bản firebase-functions của bạn dưới dạng phần phụ thuộc ngang hàng, để dự án Cloud Functions của người dùng có cùng phiên bản SDK mà thư viện của bạn được viết.
{
"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"
}
}
Ví dụ minh hoạ: Truyền phát Cloud Firestore đến BigQuery
Trước. functions/package.json của tiện ích là riêng tư, đặt tên cho mã nhận dạng tiện ích và khai báo firebase-functions làm phần phụ thuộc trực tiếp:
{
"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"
}
}
Sau. Một gói có thể xuất bản: tên có phạm vi, bản đồ exports và firebase-functions được chuyển đến 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. Nâng cấp các chức năng từ thế hệ thứ 1 lên thế hệ thứ 2
Nếu tiện ích của bạn vẫn xuất các hàm thế hệ thứ 1, hãy chuyển đổi từng trình kích hoạt sang hàm tương đương thế hệ thứ 2. Nhập từ các mô-đun firebase-functions/... và truyền các chế độ cài đặt thời gian chạy trong các lựa chọn về trình kích hoạt.
Xem Cloud Functions hướng dẫn nâng cấp lên thế hệ thứ 2. Cụ thể, bạn có thể giảm thiểu nỗ lực viết lại bằng cách sử dụng tính năng phân tách sự kiện được vá thế hệ thứ 2 và tránh viết lại logic hàm vì SDK thế hệ thứ 2 hiển thị các tham số v1 dưới dạng các trường trong đối tượng sự kiện, cho phép bạn sử dụng các tham số được phân tách/đặt tên và giữ nguyên logic kinh doanh của mình.
Trước. Thế hệ thứ 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);
});
Sau. Thế hệ thứ 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)
);
Hãy xem bảng so sánh phiên bản Cloud Functions để biết danh sách đầy đủ về những điểm khác biệt giữa các hàm thế hệ thứ 1 và thế hệ thứ 2.
Một điểm khác biệt đáng kể giữa các hàm thế hệ thứ nhất và thế hệ thứ 2 là các hàm thế hệ thứ 2 phải được đặt cùng với tài nguyên kích hoạt của chúng. Khi người dùng di chuyển từ một tiện ích sử dụng các hàm thế hệ thứ 1 sang một bộ công cụ sử dụng các hàm thế hệ thứ 2, họ có thể cần thay đổi vị trí của hàm để đáp ứng yêu cầu này.
Mọi vị trí Cloud Functions mà người dùng đã chọn có thể bị hỏng trong quá trình di chuyển vì vị trí hiện tại được giữ nguyên. Nếu các hàm do sự kiện kích hoạt của tiện ích dựa vào vị trí hàm do người dùng cung cấp, bạn nên thêm một tham số mới vào tiện ích để thu thập vị trí kích hoạt sự kiện và liên kết tham số này với vị trí hàm mới.
Ví dụ minh hoạ: Truyền phát Cloud Firestore đến BigQuery
defineString("DATABASE_REGION", {
label: "Firestore Instance Location",
description:
"Where is the Firestore database located? You can check your current database location at https://console.cloud.google.com/firestore/databases. The functions in this kit deploy to the Cloud Run region closest to this location.",
input: select({
"Multi-region (Europe - Belgium and Netherlands)": "eur3",
"Multi-region (United States)": "nam5",
"Multi-region (Iowa, North Virginia, and Oklahoma)": "nam7",
"Iowa (us-central1)": "us-central1",
// More locations...
})
});
// Firestore multi-region locations are not Cloud Run regions; deploying a
// function to one hard-fails, so they map to a region inside the multi-region.
const MULTI_REGION_TO_FUNCTION_REGION: Record<string, string> = {
nam5: "us-central1",
nam7: "us-central1",
eur3: "europe-west1",
};
/**
* Maps a Firestore database location to the Cloud Run region the functions
* should deploy to. The lookup is case-insensitive and ignores surrounding
* whitespace, as the CLI's own region handling is. Regional locations pass
* through lowercased; an unset or blank location returns `undefined`, meaning
* the functions declare no region.
*/
export function firestoreLocationToFunctionRegion(
location: string | undefined
): string | undefined {
const normalized = location?.trim().toLowerCase();
if (!normalized) {
return undefined;
}
return MULTI_REGION_TO_FUNCTION_REGION[normalized] ?? normalized;
}
const functionRegion = firestoreLocationToFunctionRegion(
process.env.DATABASE_REGION
);
export const fsexportbigquery = onDocumentWritten(
{
region: functionRegion,
// Other configuration
},
(event) => handleDocumentWrite(event, getHandlerContext())
);
Khi triển khai bộ công cụ lần đầu tiên, người dùng sẽ được nhắc nhập DATABASE_REGION và các hàm sẽ được triển khai vào functionRegion tương ứng ở trên để khắc phục mọi vấn đề về vị trí cho các hàm được kích hoạt sự kiện thế hệ thứ 2.
4. Chuyển đổi các tham số và khoá bí mật của tiện ích
Tham số
Mỗi tham số mà bạn khai báo trong extension.yaml sẽ trở thành một tham số Cloud Functions.
Chuyển đổi các chỉ số môi trường trực tiếp:
const collectionPath = process.env.COLLECTION_PATH;
vào các tham số 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);
}
);
Sử dụng collectionPath.value() để đọc chuỗi bên trong một trình xử lý; sử dụng collectionPath trực tiếp khi bạn muốn một phần giữ chỗ, chẳng hạn như đường dẫn kích hoạt hàm.
CLI Firebase sẽ phát hiện các tham số của bạn và đọc giá trị của các tham số đó từ .env, .env.<projectId> hoặc nhắc người dùng trong quá trình triển khai. Giữ nguyên tên tham số để các giá trị từ lượt cài đặt hiện có được chuyển sang.
Bạn tuyệt đối không được thay đổi tên tham số đã khai báo trong mã của mình. Quá trình di chuyển tiện ích sẽ tự động giữ nguyên các giá trị tham số hiện có của người dùng cuối, nhưng chỉ khi tên không thay đổi.
Ví dụ minh hoạ: Truyền phát Cloud Firestore đến BigQuery
Trước. Một tham số được khai báo trong extension.yaml, đọc dưới dạng biến môi trường thô trong config.ts:
# extension.yaml
- param: COLLECTION_PATH
label: Collection path
type: string
required: true
// functions/src/config.ts
collectionPath: process.env.COLLECTION_PATH,
Sau. Một defineString; CLI sẽ phát hiện và đọc từ .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 } }
}),
Tên tham số không thay đổi, vì vậy, .env hiện có vẫn hoạt động.
Mã phiên bản
Các tiện ích đọc mã nhận dạng phiên bản của chúng từ EXT_INSTANCE_ID, được Extensions runtime chèn vào. Các bộ công cụ hàm đọc mã nhận dạng phiên bản của chúng từ FIREBASE_KIT_INSTANCE_ID, mà CLI Firebase đặt cho mỗi phiên bản bộ công cụ thành khoá của phiên bản trong bản đồ instances trong firebase.json. CLI cung cấp thông tin này trong quá trình phát hiện thời gian triển khai, trong trình mô phỏng và cho các hàm đã triển khai.
Mã phiên bản không phải là một tham số, vì vậy, đừng khai báo mã này bằng defineString. Trên thực tế, FIREBASE_... là một tiền tố dành riêng trong các tệp .env, vì vậy, người dùng sẽ không thể đặt hoặc ghi đè tiền tố này. Hệ thống tham số không thấy được các giá trị mà CLI chèn. Đọc trực tiếp từ môi trường:
// Before
const instanceId = process.env.EXT_INSTANCE_ID;
// After
const instanceId = process.env.FIREBASE_KIT_INSTANCE_ID;
Biến này sẽ chỉ được đặt khi gói của bạn được triển khai dưới dạng một bộ. Nếu mã của bạn cũng được triển khai dưới dạng một cơ sở mã độc lập (xem Bước 9), hãy coi mã đó là không bắt buộc hoặc nhanh chóng thất bại với một thông báo rõ ràng khi mã đó bị thiếu. Nếu tiện ích của bạn hiển thị mã nhận dạng phiên bản dưới dạng một tham số mà người dùng nhìn thấy, thì bạn phải xoá tham số đó vì CLI Firebase hiện sở hữu giá trị này.
Ví dụ minh hoạ: Xoá dữ liệu người dùng
(Tiện ích Cloud Firestore đến BigQuery không đọc mã nhận dạng phiên bản của tiện ích, nên không có gì để di chuyển ở đó. Tiện ích Xoá dữ liệu người dùng dùng tên này cho các chủ đề Pub/Sub.)
Trước. Đọc dưới dạng biến môi trường thô trong config.ts bằng tiền tố ext- mà Tiện ích dùng cho tài nguyên của tiện ích:
// functions/src/config.ts
discoveryTopic: `ext-${process.env.EXT_INSTANCE_ID}-discovery`,
deletionTopic: `ext-${process.env.EXT_INSTANCE_ID}-deletion`,
Sau. Một thao tác process.env đọc đơn giản của FIREBASE_KIT_INSTANCE_ID dùng cho 2 tham số thông thường để người dùng có thể ghi đè tên chủ đề:
// 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`,
}),
Giá trị mặc định không được trống vì các liên kết kích hoạt sẽ được phân giải tại thời điểm phát hiện. Một giá trị mặc định trống sẽ được ghi vào tệp kê khai triển khai dưới dạng tên chủ đề. Bộ công cụ này cũng có khả năng phòng vệ khi chạy bên ngoài bối cảnh của một bộ công cụ. Nếu thiếu biến, giá trị mặc định ở cấp mô-đun sẽ đánh giá thành kit-undefined-discovery, do đó, trình tải cấu hình sẽ gặp lỗi kèm theo thông báo lỗi giải thích thay vì:
// ...
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."
);
}
// ...
Thao tác kiểm tra này sẽ chạy khi trình xử lý giải quyết cấu hình lần đầu tiên, vì vậy, một biến bị thiếu sẽ tạo ra lỗi thời gian chạy rõ ràng thay vì các hàm được liên kết âm thầm với các chủ đề kit-undefined-*. Để từ chối chính việc triển khai, hãy thực hiện quy trình kiểm tra ở phạm vi mô-đun để quy trình này chạy trong quá trình khám phá. Vì CLI lấy mã nhận dạng phiên bản từ firebase.json, nên không có INSTANCE_ID có thể định cấu hình và không có gì cần đồng bộ hoá trên nhiều phiên bản.
Bí mật
Trong extension.yaml, bạn khai báo các khoá bí mật bằng type: secret. Thời gian chạy Tiện ích sẽ lưu trữ và liên kết các tiện ích này, nhờ đó mã tiện ích của bạn có thể đọc trực tiếp process.env.PARAM_NAME. Trong một cơ sở mã Cloud Functions thông thường, bạn khai báo và liên kết từng khoá bí mật một cách rõ ràng:
import { defineSecret } from "firebase-functions/params";
import { onRequest } from "firebase-functions/https";
const apiKey = defineSecret("API_KEY");
export const fn = onRequest({ secrets: [apiKey] }, handler);
Sau khi tiện ích của bạn được di chuyển sang một gói/bộ công cụ npm, các thông tin tham chiếu bí mật sẽ được quản lý trong tệp .env của người dùng cuối. Bạn không được thay đổi tên bí mật được khai báo trong mã của mình dưới bất kỳ hình thức nào. Trong quá trình di chuyển, các khoá bí mật của người dùng cuối sẽ được di chuyển theo.
Ví dụ minh hoạ: Kích hoạt email từ Cloud Firestore
Trước. MAIL_COLLECTION và SMTP_PASSWORD được đọc dưới dạng các biến môi trường thô trong 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,
Sau. Một defineString và một defineSecret; CLI sẽ phát hiện cả hai và đọc từ .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. Di chuyển các lệnh gọi hàng đợi tác vụ nội bộ
Một số tiện ích xếp hàng công việc vào hàng đợi tác vụ riêng từ bên trong mã hàm của chúng, bằng cách sử dụng Firebase Admin SDK. Điều này khác với việc nhận một tác vụ được gửi đi (được đề cập trong các phần Nâng cấp hàm và Chuyển đổi các lệnh gọi lại vòng đời). Trong đó, mã của bạn là producer (thực thể tạo) gọi queue.enqueue(...).
Các phiên bản trước của Admin SDK yêu cầu các tiện ích truyền mã nhận dạng phiên bản tiện ích riêng dưới dạng tham số thứ hai để nhắm đến một hàm Hàng đợi tác vụ trong cùng một tiện ích. Kể từ phiên bản firebase-admin 14.2.0, bạn không bắt buộc cũng như không nên sử dụng phương thức này. Theo mặc định, Task Queue API hiện nhắm đến các hàng đợi tác vụ trong cùng một bối cảnh (ví dụ: một tiện ích hoặc bộ công cụ). Bạn nên xoá tham số này trong mã của mình (cả dưới dạng một tiện ích và dưới dạng các hàm độc lập) để đảm bảo an toàn.
Việc xoá tham số này sẽ đảm bảo khả năng di động và khả năng tương thích trong tương lai.
Mọi thứ khác về lệnh gọi enqueue (xếp hàng vào), chẳng hạn như đường dẫn tài nguyên locations/<region>/functions/<name>, tải trọng tác vụ và logic thử lại của bạn, vẫn giữ nguyên.
Hãy xem bài viết Xếp hàng đợi cho các hàm bằng Cloud Tasks để biết thêm thông tin về cách xếp hàng đợi cho các hàm bằng Cloud Tasks.
Trước. Tiện ích thế hệ thứ 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);
Sau. Tiện ích thế hệ thứ 2:
import { getFunctions } from "firebase-admin/functions";
const queue = getFunctions().taskQueue(
`locations/${process.env.FUNCTION_REGION}/functions/syncBigQuery`
);
await queue.enqueue(taskData);
Nếu lệnh gọi xếp hàng của bạn nhắm đến một toàn bộ mã nguồn có tiền tố, thì tên hàm được phát hiện cũng có tiền tố (ví dụ: orders-syncBigQuery); hãy xem phần Xem xét và cài đặt phiên bản bộ hàm thay thế và Kiểm thử dưới dạng bộ hàm.
6. Khai báo các API và vai trò IAM bắt buộc
Di chuyển các yêu cầu về IAM và API của tiện ích ra khỏi extension.yaml và vào mã:
import { requiresAPI, requiresRole } from "firebase-functions";
requiresAPI("bigquery.googleapis.com", "Needed to write changelog rows");
requiresRole("roles/bigquery.dataEditor");
requiresRole("roles/bigquery.user");
Với tính năng bảo mật khai báo, CLI Firebase sẽ tạo hoặc cập nhật tài khoản dịch vụ thời gian chạy được quản lý cho toàn bộ mã nguồn và cấp cho tài khoản đó tất cả các vai trò đã khai báo. Tài liệu cho người dùng rằng tất cả các hàm trong cơ sở mã đều chạy với những vai trò đó, trừ phi API cuối cùng hỗ trợ một mô hình hẹp hơn.
Ví dụ minh hoạ: Truyền phát Cloud Firestore đến BigQuery
Trước. Được khai báo trong extension.yaml; thời gian chạy Tiện ích đã bật API và cấp vai trò cho một tài khoản được quản lý:
apis:
- apiName: bigquery.googleapis.com
roles:
- role: bigquery.dataEditor
- role: datastore.user
- role: bigquery.user
Sau. Khai báo trong mã bằng requiresAPI và 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");
Nếu tiện ích của bạn xuất bản các sự kiện Eventarc, thì bạn cũng cần thiết lập các vai trò và API bắt buộc thích hợp để xuất bản các sự kiện. Trước đây, việc này được Extensions xử lý mà không cần bạn phải thay đổi gì trong extensions.yaml. Bạn có thể thực hiện việc này có điều kiện trong mã của mình để bộ công cụ chỉ yêu cầu các quyền này khi một kênh Eventarc tuỳ chỉnh được sử dụng.
if (!!process.env.EVENTARC_CHANNEL) {
requiresRole("roles/eventarc.publisher");
requiresAPI(
"eventarcpublishing.googleapis.com",
"Publishes the extension's custom events to its Eventarc channel."
);
}
7. Chuyển đổi các hook vòng đời
Nếu tiện ích của bạn gọi getExtensions().runtime() (ví dụ: setProcessingState hoặc setFatalError), hãy xoá các lệnh gọi đó vì chúng sẽ gây ra lỗi nếu được gọi từ một hàm thế hệ thứ 2 được triển khai bình thường. Trạng thái vòng đời hiện do afterFirstDeploy và afterRedeploy điều khiển, trong đó tính năng theo dõi trạng thái này không được dùng.
Firebase Extensions có thể chạy quy trình thiết lập khi người dùng cài đặt, cập nhật hoặc định cấu hình lại một tiện ích. Trong gói npm, hãy khai báo các thao tác tương đương trong vòng đời trong mã.
Đối với chế độ thiết lập một lần:
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: {}
}
});
Đối với nội dung cập nhật cấu hình hoặc mã:
import { afterRedeploy } from "firebase-functions/lifecycle";
afterRedeploy({
task: {
function: "runInitialSetup",
body: { reconcile: true }
}
});
Hãy đảm bảo các thao tác trong vòng đời của bạn là bất biến. Người dùng có thể cần chạy lại các thao tác này theo cách thủ công nếu quá trình gửi hoặc thực thi không thành công:
firebase functions:lifecycle:run afterFirstDeploy CODEBASE_NAME
firebase functions:lifecycle:run afterRedeploy CODEBASE_NAME
Ví dụ minh hoạ: Truyền phát Cloud Firestore đến BigQuery
Trước. lifecycleEvents trong extension.yaml, do Extensions runtime điều khiển:
lifecycleEvents:
onInstall:
function: initBigQuerySync
processingMessage: Configuring BigQuery Sync.
onUpdate:
function: setupBigQuerySync
processingMessage: Configuring BigQuery Sync
onConfigure:
function: setupBigQuerySync
processingMessage: Configuring BigQuery Sync
Sau. Khai báo trong mã; nhiệm vụ cung cấp BigQuery khi triển khai lần đầu:
import { afterFirstDeploy, afterRedeploy } from "firebase-functions/lifecycle";
afterFirstDeploy({ task: { function: "initBigQuerySync" } });
afterRedeploy({ task: { function: "setupBigQuerySync" } });
Việc cung cấp là bất biến, vì vậy, việc chạy lại sẽ điều chỉnh tập dữ liệu, bảng và khung hiển thị.
Người dùng có thể chạy lại theo cách thủ công bằng tổ hợp phím firebase functions:lifecycle:run afterFirstDeploy
CODEBASE_NAME.
8. Thiết lập tài liệu cho người dùng
Viết một gói README giải thích ít nhất:
- Giá trị
.envmà gói yêu cầu. - Các khoá bí mật mà gói yêu cầu và cách di chuyển các giá trị khoá bí mật hiện có.
- Các vai trò IAM mà gói khai báo bằng
requiresRole(...). - Các API của Google mà gói này cho phép hoặc yêu cầu.
- Các lệnh gọi lại trong vòng đời mà gói khai báo và cách chạy lại các lệnh gọi lại đó theo cách thủ công.
- Ghi chú về việc thanh toán.
- Những điểm thay đổi so với tiện ích gốc.
- Cách gói nhận mã nhận dạng phiên bản (
FIREBASE_KIT_INSTANCE_IDdo CLI đặt) và tất cả các hàm của phiên bản đều được triển khai bằng tiền tốkit-<instanceId>-.
Ví dụ minh hoạ: Truyền phát Cloud Firestore đến BigQuery
Gói README gửi một bảng "nội dung thay đổi" cụ thể:
| Mối lo ngại | Dưới dạng tiện ích | Như @firebase-function-kits/firestore-bigquery-export |
|---|---|---|
| Config | Tham số tiện ích | Cloud Functions tham số thông qua .env |
| IAM | Được cấp bởi tiện ích | requiresRole(...), được áp dụng khi triển khai |
| Cung cấp | Tác vụ vòng đời theo Tiện ích | afterFirstDeploy / afterRedeploy việc cần làm |
| Tên hàm | ext-<instanceId>-fsexportbigquery |
fsexportbigquery (có thể có tiền tố) |
| Mã phiên bản | EXT_INSTANCE_ID do Tiện ích chèn |
FIREBASE_KIT_INSTANCE_ID, do CLI đặt từ firebase.json |
9. Kiểm thử hàm thế hệ thứ 2
Giờ đây, bạn sẽ có một hàm thế hệ thứ 2. Khi được triển khai, hàm này sẽ hoạt động giống hệt như một bản cài đặt mới của tiện ích. Bước tiếp theo là xác minh và khắc phục mọi vấn đề vô tình xảy ra trong quá trình này.
Để gọi setGlobalOptions để đặt các lựa chọn chung, chẳng hạn như khu vực hoặc CPU mặc định, bạn chỉ được làm như vậy khi triển khai bộ công cụ dưới dạng một hàm độc lập thế hệ thứ 2. Khi bộ công cụ của bạn được cài đặt dưới dạng một gói npm, người dùng sẽ gọi setGlobalOptions trong mã bao bọc của họ để định cấu hình các tham số này và họ sẽ nhận được cảnh báo nếu điều này xảy ra hai lần.
Bạn có thể bảo vệ lệnh gọi này bằng cách kiểm tra biến môi trường FIREBASE_KIT_INSTANCE_ID:
import { setGlobalOptions } from "firebase-functions";
if (!process.env.FIREBASE_KIT_INSTANCE_ID) {
setGlobalOptions({
region: "us-east1",
maxInstances: 10,
});
}
Đảm bảo bạn đang sử dụng firebase-tools
>= 15.32.0 và triển khai hàm thế hệ thứ 2 đã chuyển đổi vào một dự án kiểm thử bằng các tài nguyên phù hợp để kiểm thử hành vi của hàm đó. Nếu bạn đã thiết lập một dự án kiểm thử từ việc kiểm thử tiện ích của mình, hãy chạy lệnh sau:
firebase deploy --only functions
Điền vào trình hướng dẫn kết quả, trình hướng dẫn này sẽ nhắc bạn nhập các giá trị tham số theo cách tương tự như cách bạn đã điền vào biểu mẫu cài đặt trong bảng điều khiển Firebase cho tiện ích.
Ví dụ minh hoạ: Truyền phát Cloud Firestore đến BigQuery
Chúng tôi xác minh quá trình đồng bộ hoá Cloud Firestore với BigQuery theo phương thức mã hoá hai đầu:
- Trong trang Cloud Firestore của bảng điều khiển Firebase, hãy tạo bộ sưu tập mà bạn đặt làm
COLLECTION_PATH(users) nếu bộ sưu tập đó chưa tồn tại. - Tạo một tài liệu có tên là
bigquery-mirror-testchứa mọi trường có mọi giá trị. Trong trang BigQuery của bảng điều khiển Google Cloud, hãy truy vấn bảng nhật ký thay đổi thô. Tệp này phải chứa một hàng duy nhất ghi lại quá trình tạo tài liệu:
SELECT * FROM `PROJECT_ID.analytics.users_raw_changelog`Truy vấn chế độ xem mới nhất. Truy vấn này sẽ trả về sự kiện thay đổi mới nhất cho tài liệu duy nhất hiện có (
bigquery-mirror-test):SELECT * FROM `PROJECT_ID.analytics.users_raw_latest`Xoá tài liệu
bigquery-mirror-testtrong Cloud Firestore. Sự kiện này sẽ biến mất khỏi chế độ xem mới nhất và sự kiệnDELETEsẽ được thêm vào bảng nhật ký thay đổi thô.Bạn có thể kiểm tra toàn bộ nhật ký của một tài liệu bằng cách:
SELECT * FROM `PROJECT_ID.analytics.users_raw_changelog` WHERE document_name = "bigquery-mirror-test" ORDER BY timestamp ASC
Điểm khác biệt so với việc thử nghiệm tiện ích:
- Trình kích hoạt triển khai dưới dạng
fsexportbigquery(không có tiền tố khi triển khai dưới dạng hàm thế hệ thứ 2 thông thường và không phải là một bộ công cụ), không phảiext-<instanceId>-fsexportbigquery. Tìm tên đó trong trang tổng quan và nhật ký Cloud Functions. - Giờ đây, mã của bạn sẽ chạy trong Firebase Local Emulator Suite dưới dạng các hàm thông thường.
Bạn có thể đặt giá trị của các tham số để sử dụng trong trình mô phỏng bằng
.env.local. Bạn cũng có thể kiểm thử đơn vị mã bằng SDKfirebase-functions-testnhư mô tả trong phần Kiểm thử đơn vị Cloud Functions. - Quá trình cung cấp không còn được điều khiển bởi thời gian chạy Tiện ích nữa. Nếu bảng nhật ký thay đổi bị thiếu sau khi triển khai, hãy chạy lại tác vụ thiết lập theo cách thủ công:
firebase functions:lifecycle:run afterFirstDeploy CODEBASE_NAME. Tác vụ này là tác vụ bất biến, vì vậy, việc chạy lại tác vụ này sẽ điều chỉnh tập dữ liệu, bảng và khung hiển thị. - Các giá trị tham số đến từ
.envchứ không phải biểu mẫu cài đặt, vì vậy, các lần chạy lạifirebase deploykhông có tính tương tác sau khi.envhoàn tất.
10. Xuất bản bộ hàm của bạn trên npm
Sau khi xác thực lượt chuyển đổi từ tiện ích sang hàm thế hệ thứ 2, bạn có thể phát hành bản phát hành dùng thử lên npm để kiểm thử toàn diện bằng một trong các hướng dẫn sau:
Khi được xuất bản lên npm, bộ công cụ của bạn có thể được cài đặt bằng firebase functions:kits:install và được liệt kê là giải pháp thay thế chính thức cho tiện ích của bạn.
Bạn nên xuất bản bản phát hành dùng thử trước. Các bộ công cụ được cài đặt theo tên và phiên bản gói, vì vậy, một bản phát hành trước cho phép bạn kiểm thử quy trình cài đặt thực tế dựa trên sổ đăng ký mà không để lộ một gói chưa hoàn chỉnh cho những người dùng cài đặt bằng thẻ latest mặc định.
Trước khi xuất bản:
- Chọn tên gói. Cả tên có phạm vi và tên không có phạm vi đều hoạt động (xem hướng dẫn ở trên). Xin lưu ý rằng theo mặc định, các gói có phạm vi đều ở chế độ riêng tư, vì vậy, hãy truyền
--access public. - Tạo và kiểm tra những gì được vận chuyển.
mainvàtypestrỏ đến đầu ra đã biên dịch (lib/trong ví dụ minh hoạ của chúng tôi), vì vậy, thư mục đó phải có trong tệp tar đã xuất bản. Sử dụng.npmignorehoặc danh sách cho phépfilesvà kiểm tra kết quả bằngnpm pack --dry-run. Tập lệnhprepublishOnlychạy bản dựng của bạn sẽ ngăn việc xuất bản đầu ra cũ.
Những điểm bổ sung package.json giúp việc xuất bản trở nên an toàn theo mặc định:
{
"files": ["lib", "README.md", "CHANGELOG.md"],
"publishConfig": { "access": "public", "tag": "next" },
"scripts": {
"build": "tsc -b",
"prepublishOnly": "npm run build && npm test"
}
}
Trường "publishConfig": { "tag": "next" } đảm bảo rằng npm
publish thuần tuý không bao giờ ghi đè latest.
Cắt bản phát hành dùng thử:
Ví dụ: để tăng phiên bản cục bộ từ 0.0.2-rc.3 lên 0.0.2-rc.4 (thao tác này sẽ cam kết và gắn thẻ trong Git nếu package.json nằm ở thư mục gốc của kho lưu trữ):
npm version prerelease --preid rc
Để xuất bản bản phát hành dùng thử và đăng ký @your-org/your-kit@0.0.2-rc.4 trên npm trong thẻ next:
npm publish
Trang web npm có thể mất vài phút để hiển thị phiên bản mới; npm view đọc trực tiếp sổ đăng ký:
npm view @your-org/your-kit versions dist-tags
Sau khi hoàn tất Bước 11 và Bước 12 trong hướng dẫn này, bạn có thể chuyển gói thành phiên bản ổn định:
npm version 0.0.2
npm publish --tag latest
npm dist-tag add @your-org/your-kit@0.0.2 next
Lưu ý về npm-shrinkwrap.json: Bạn nên đưa tệp npm-shrinkwrap.json vào gói của mình; CLI sẽ cảnh báo người dùng khi cài đặt nếu bạn không làm như vậy. Việc này đảm bảo người dùng sử dụng đúng các phần phụ thuộc mà bạn đã kiểm thử và giúp bảo vệ khỏi các cuộc tấn công vào chuỗi cung ứng. Tuy nhiên, shrinkwrap được áp dụng nguyên văn trong các dự án của người dùng, kể cả trong quá trình tạo bản dựng Cloud Functions (npm ci), trong đó các mục nhập chỉ dành cho nhà phát triển có thể không thành công với EBADPLATFORM. Bạn có thể cần xoá các mục "dev": true và devDependencies khỏi bản sao shrinkwrap đã xuất bản.
Ví dụ minh hoạ: Truyền phát Cloud Firestore đến BigQuery
package.json của bộ công cụ tại thời điểm phát hành bản phát hành dùng thử thứ tư:
{
"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" }
}
Xin lưu ý rằng trong trường hợp này, bộ công cụ nằm trong một kho lưu trữ đơn, vì vậy, bạn cần phải thêm repository.directory cho đường liên kết đến sổ đăng ký npm để trỏ đến thư mục chính xác. CHANGELOG.md chứa các ghi chú cho bản phát hành đang chờ xử lý.
11. Kiểm thử dưới dạng một bộ chức năng
Sau khi xuất bản bộ công cụ, bạn nên kiểm thử bộ công cụ bằng npm.
Đảm bảo rằng bạn đang sử dụng phiên bản firebase-tools >= 15.32.0 và cài đặt bộ công cụ:
firebase functions:kits:install --package <your-package-name>@<your-prerelease-version>
Lệnh này sẽ tải gói của bạn xuống từ npm, thiết lập gói trong một thư mục nguồn mới cho bộ công cụ và hướng dẫn bạn cách định cấu hình phiên bản đầu tiên tương tự như quy trình cài đặt tiện ích. Sau khi bạn cài đặt và thiết lập gói cục bộ, hãy chạy một lệnh triển khai để tạo tài nguyên trong dự án Google Cloud:
firebase deploy --only functions:<your-kit-instance-id>
Sau khi cài đặt, CLI Firebase sẽ in một lệnh triển khai tương tự với mã nhận dạng phiên bản chính xác mà bạn đã chọn trong quá trình cài đặt.
Xác thực lại bộ công cụ theo hướng dẫn trong Bước 9. Kiểm thử hàm thế hệ thứ 2. Giờ đây, khi bạn triển khai bằng các bộ công cụ, các hàm của bạn sẽ có tiền tố và được đặt tên là kit-<instance-id>-<method-name>. Điều này cho phép các thành phần có nhiều thực thể, triển khai cùng một hàm nhiều lần trong một dự án, mỗi thực thể có một tên riêng.
12. Kiểm thử việc thay thế quy trình di chuyển
Bạn có thể thiết lập một phiên bản tiện ích đang hoạt động rồi sử dụng hướng dẫn di chuyển người dùng (sử dụng firebase ext:migrate --package hoặc các lệnh CLI của bộ công cụ hàm) để hoàn tất việc kiểm thử bộ công cụ hàm dưới dạng một giải pháp thay thế di chuyển.
13. Thông báo cho người dùng và Google về tiện ích thay thế chính thức của bạn
Sau khi bộ chức năng thay thế của bạn đã sẵn sàng và có sẵn dưới dạng một gói npm mà người dùng nên di chuyển sang, hãy thông báo cho cả người dùng và Google về bộ chức năng thay thế chính thức này. Cập nhật tệp README.md trong kho lưu trữ GitHub lưu trữ tiện ích của bạn bằng thông tin sau:
<!-- 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 quét các tệp README của tiện ích đã biết để tìm những nhận xét như <!--
FIREBASE_EXTENSION_REPLACEMENT: extension="firebase/firestore-bigquery-export"
package="@firebase-function-kits/firestore-bigquery-export" --> và sử dụng nhận xét này để điền vào sổ đăng ký chính thức của chúng tôi về các thành phần thay thế được lưu trữ trong kho lưu trữ firebase-tools dưới dạng replacements.json.
Bạn cũng có thể kiểm tra replacements.json để xem README.md nào sẽ được quét cho tiện ích của bạn. Danh sách chính thức về các từ thay thế được cập nhật hằng tuần.
Ví dụ minh hoạ: Truyền phát Cloud Firestore đến BigQuery
Tiện ích firestore-bigquery-exportREADME.md chứa:
<!-- 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.