在擴充功能中新增使用者掛鉤
透過集合功能整理內容
你可以依據偏好儲存及分類內容。
您可以讓安裝擴充功能的使用者,在擴充功能執行時插入自己的自訂邏輯。有兩種方法可以達成這個目標:
擴充功能可以採用其中一種或兩種方法。
Eventarc 事件
如要從擴充功能發布事件,請按照下列步驟操作:
在 extension.yaml
檔案中宣告您要發布的事件類型:
events:
- type: publisher-id.extension-name.version.event-name
description: event-description
- type: publisher-id.extension-name.version.another-event-name
description: another-event-description
type
ID 由多個以半形句號分隔的欄位組成。請務必填寫「發布商 ID」、「擴充功能名稱」和「事件名稱」欄位。建議填寫版本欄位。為發布的每種事件類型選擇專屬且具描述性的事件名稱。
舉例來說,storage-resize-images
擴充功能會宣告單一事件類型:
events:
- type: firebase.extensions.storage-resize-images.v1.complete
description: |
Occurs when image resizing completes. The event will contain further
details about specific formats and sizes.
使用者安裝擴充功能後,就能選擇要訂閱哪些活動。
在擴充功能中,從 Admin SDK 匯入 Eventarc API,並使用使用者的安裝設定初始化事件通道。這些設定會透過下列環境變數公開:
EVENTARC_CHANNEL
:使用者選擇發布事件的 Eventarc 管道完整名稱。
EXT_SELECTED_EVENTS
:以半形逗號分隔的事件類型清單,列出使用者選擇發布的事件類型。使用這個值初始化管道時,Admin SDK 會自動篩除使用者未選取的事件。
EVENTARC_CLOUD_EVENT_SOURCE
:Cloud Event 來源 ID。Admin SDK 會在發布事件的 source
欄位中自動傳遞這個值。您通常不需要明確使用這個變數。
如果安裝時未啟用事件,這些變數會是未定義。只有在啟用事件時,才能使用這項事實初始化事件管道:
import * as admin from "firebase-admin";
import {getEventarc} from 'firebase-admin/eventarc';
admin.initializeApp();
// Set eventChannel to a newly-initialized channel, or `undefined` if events
// aren't enabled.
const eventChannel =
process.env.EVENTARC_CHANNEL &&
getEventarc().channel(process.env.EVENTARC_CHANNEL, {
allowedEventTypes: process.env.EXT_SELECTED_EVENTS,
});
在擴充功能中,將要向使用者顯示的事件發布至管道。例如:
// If events are enabled, publish a `complete` event to the configured
// channel.
eventChannel && eventChannel.publish({
type: 'firebase.extensions.storage-resize-images.v1.complete',
subject: filename, // the name of the original file
data: {
// ...
}
});
在 PREINSTALL 或 POSTINSTALL 檔案中記錄您發布的事件。
請記錄每個事件的下列資訊:
- 預期用途
- 擴充功能邏輯的執行時間點
- 輸出資料
- 執行條件
此外,也請提醒使用者,不要在事件處理常式中執行任何可能觸發相同擴充功能的動作,以免造成無限迴圈。
從擴充功能發布事件時,使用者可以部署事件處理常式,以自訂邏輯回應。
舉例來說,以下範例會在原始圖片調整大小後刪除該圖片。請注意,這個範例處理常式會使用事件的 subject
屬性,在本例中為圖片的原始檔案名稱。
exports.onimageresized = onCustomEventPublished(
"firebase.extensions.storage-resize-images.v1.complete",
(event) => {
logger.info("Received image resize completed event", event);
// For example, delete the original.
return admin.storage()
.bucket("my-project.firebasestorage.app")
.file(event.subject)
.delete();
});
詳情請參閱「自訂事件觸發條件」。
範例
官方的 Resize Images 擴充功能會在調整圖片大小後發布至 Eventarc,提供非同步掛鉤。
同步掛鉤
如要為使用者提供必須成功完成的掛鉤,才能運作其中一個擴充功能,請使用同步掛鉤。
同步掛鉤會呼叫使用者定義的 HTTPS 可呼叫 Cloud Function,並等待完成 (可能會有傳回值),然後再繼續。使用者提供的函式發生錯誤,導致擴充功能函式發生錯誤。
如要公開同步掛鉤:
在擴充功能中新增參數,讓使用者透過自訂 Cloud 函式的網址設定擴充功能。例如:
- param: PREPROCESSING_FUNCTION
label: Pre-processing function URL
description: >
An HTTPS callable function that will be called to transform the input data
before it is processed by this function.
type: string
example: https://us-west1-my-project-id.cloudfunctions.net/preprocessData
required: false
在擴充功能中要公開 Hook 的位置,使用網址呼叫函式。例如:
const functions = require('firebase-functions/v1');
const fetch = require('node-fetch');
const preprocessFunctionURL = process.env.PREPROCESSING_FUNCTION;
exports.yourFunctionName = functions.firestore.document("collection/{doc_id}")
.onWrite((change, context) => {
// PREPROCESSING_FUNCTION hook begins here.
// If a preprocessing function is defined, call it before continuing.
if (preprocessFunctionURL) {
try {
await fetch(preprocessFunctionURL); // Could also be a POST request if you want to send data.
} catch (e) {
// Preprocessing failure causes the function to fail.
functions.logger.error("Preprocessor error:", e);
return;
}
}
// End of PREPROCESSING_FUNCTION hook.
// Main function logic follows.
// ...
});
在 PREINSTALL 或 POSTINSTALL 檔案中,記錄您提供的任何掛鉤。
針對每個掛鉤,請記錄下列事項:
- 預期用途
- 擴充功能邏輯的執行時間點
- 預期的輸入和輸出內容
- 執行條件 (或選項)
此外,請警告使用者不要在掛鉤函式中執行任何可能觸發相同擴充功能的動作,以免造成無限迴圈。
範例
Algolia Search 擴充功能提供同步掛鉤,可在寫入 Algolia 之前呼叫使用者提供的轉換函式。
除非另有註明,否則本頁面中的內容是採用創用 CC 姓名標示 4.0 授權,程式碼範例則為阿帕契 2.0 授權。詳情請參閱《Google Developers 網站政策》。Java 是 Oracle 和/或其關聯企業的註冊商標。
上次更新時間:2025-07-25 (世界標準時間)。
[null,null,["上次更新時間:2025-07-25 (世界標準時間)。"],[],[],null,["\u003cbr /\u003e\n\nYou can provide users who install your extension the ability to insert their own\ncustom logic into the execution of your extension. There are two ways to\naccomplish this:\n\n- **Eventarc events**: to give users a way to asynchronously react to\n events, you can publish to Eventarc. Users can deploy event handler\n functions that, for example, send notifications after long-running\n tasks complete, or they can define their own post-processing functions.\n\n- **Synchronous hooks**: to give users a way to add blocking logic to your\n extension, you can add synchronous hooks at predefined points in the\n extension's operation. At these points, you run a user-provider function\n and proceed only after it completes. Pre-processing tasks often fall under\n this category.\n\nAn extension can use either or both methods.\n\nEventarc events\n\nTo publish events from an extension:\n\n1. Declare the event types you will publish in the `extension.yaml` file:\n\n events:\n - type: publisher-id.extension-name.version.event-name\n description: event-description\n - type: publisher-id.extension-name.version.another-event-name\n description: another-event-description\n\n The `type` identifier is made of several dot-delimited fields. The\n [publisher ID](/docs/extensions/publishers/register), extension name, and event name fields are\n required. The version field is recommended. Choose a unique and descriptive\n event name for each event type you publish.\n\n For example, the [`storage-resize-images` extension](https://github.com/firebase/extensions/blob/next/storage-resize-images/extension.yaml)\n declares a single event type: \n\n events:\n - type: firebase.extensions.storage-resize-images.v1.complete\n description: |\n Occurs when image resizing completes. The event will contain further\n details about specific formats and sizes.\n\n Users will be able to choose which events to subscribe to when they\n install the extension.\n2. In your extension functions, import the Eventarc API from the Admin SDK\n and initialize an event channel using the user's installation settings.\n These settings are exposed using the following environment variables:\n\n - `EVENTARC_CHANNEL`: the fully-qualified name of the Eventarc channel to which the user chose to publish events.\n - `EXT_SELECTED_EVENTS`: a comma-separated list of event types the user chose to publish. When you initialize a channel with this value, the Admin SDK automatically filters out events user did not select.\n - `EVENTARC_CLOUD_EVENT_SOURCE`: the Cloud Event source identifier. The Admin SDK automatically passes this value in the `source` field of published events. You typically don't need to explicitly use this variable.\n\n If events weren't enabled at installation, these variables will be\n undefined. You can use this fact to initialize an event channel only when\n events are enabled: \n\n import * as admin from \"firebase-admin\";\n import {getEventarc} from 'firebase-admin/eventarc';\n\n admin.initializeApp();\n\n // Set eventChannel to a newly-initialized channel, or `undefined` if events\n // aren't enabled.\n const eventChannel =\n process.env.EVENTARC_CHANNEL &&\n getEventarc().channel(process.env.EVENTARC_CHANNEL, {\n allowedEventTypes: process.env.EXT_SELECTED_EVENTS,\n });\n\n3. Publish events to the channel at the points in your extension you want to\n expose to users. For example:\n\n // If events are enabled, publish a `complete` event to the configured\n // channel.\n eventChannel && eventChannel.publish({\n type: 'firebase.extensions.storage-resize-images.v1.complete',\n subject: filename, // the name of the original file\n data: {\n // ...\n }\n });\n\n4. Document the events you publish, in either the PREINSTALL or POSTINSTALL\n file.\n\n For each event, document the following:\n - Its intended purpose\n - The point in your extension's logic it runs\n - The output data it includes\n - The conditions for its execution\n\n Additionally, warn users not to perform any actions in their event\n handlers that might trigger the same extension, resulting in an infinite\n loop.\n\nWhen you publish events from an extension, users can deploy event handlers\nto respond with custom logic.\n\nFor example, the following example deletes the original image after it has been\nresized. Note that this example handler makes use of the `subject` property of\nthe event, which in this case is the image's original filename. \n\n exports.onimageresized = onCustomEventPublished(\n \"firebase.extensions.storage-resize-images.v1.complete\",\n (event) =\u003e {\n logger.info(\"Received image resize completed event\", event);\n // For example, delete the original.\n return admin.storage()\n .bucket(\"my-project.firebasestorage.app\")\n .file(event.subject)\n .delete();\n });\n\nSee [Custom event triggers](/docs/functions/custom-events#handle-events) for more\ninformation.\n\nExample\n\nThe official [Resize Images extension](https://github.com/firebase/extensions/tree/next/storage-resize-images)\nprovides an asynchronous hook by [publishing to Eventarc](https://github.com/firebase/extensions/blob/c29781c7e67c004e2491e4ce3c43b25b05bd3de6/storage-resize-images/functions/src/index.ts#L109-L117)\nafter resizing an image.\n\nSynchronous hooks\n\nWhen you want to provide users with a hook that must complete successfully\nfor one of your extension functions to operate, use *synchronous hooks*.\n\nA synchronous hook calls a user-defined [HTTPS callable Cloud\nFunction](/docs/functions/http-events) and awaits completion (possibly with a\nreturned value) before continuing. An error in the user-provided function\nresults in an error in the extension function.\n\nTo expose a synchronous hook:\n\n1. Add a parameter to your extension that allows users to configure the\n extension with the URL to their custom Cloud Function. For example:\n\n - param: PREPROCESSING_FUNCTION\n label: Pre-processing function URL\n description: \u003e\n An HTTPS callable function that will be called to transform the input data\n before it is processed by this function.\n type: string\n example: https://us-west1-my-project-id.cloudfunctions.net/preprocessData\n required: false\n\n2. At the point in your extension where you want to expose the hook, call the\n function using its URL. For example:\n\n const functions = require('firebase-functions/v1');\n const fetch = require('node-fetch');\n\n const preprocessFunctionURL = process.env.PREPROCESSING_FUNCTION;\n\n exports.yourFunctionName = functions.firestore.document(\"collection/{doc_id}\")\n .onWrite((change, context) =\u003e {\n // PREPROCESSING_FUNCTION hook begins here.\n // If a preprocessing function is defined, call it before continuing.\n if (preprocessFunctionURL) {\n try {\n await fetch(preprocessFunctionURL); // Could also be a POST request if you want to send data.\n } catch (e) {\n // Preprocessing failure causes the function to fail.\n functions.logger.error(\"Preprocessor error:\", e);\n return;\n }\n }\n // End of PREPROCESSING_FUNCTION hook.\n\n // Main function logic follows.\n // ...\n });\n\n3. Document any hooks you make available in either the PREINSTALL or\n POSTINSTALL file.\n\n For each hook, document the following:\n - Its intended purpose\n - The point in your extension's logic it runs\n - Its expected inputs and outputs\n - The conditions (or options) for its execution\n\n Additionally, warn users not to perform any actions in the hook\n function that might trigger the same extension, resulting in an infinite\n loop.\n\nExample\n\nThe [Algolia Search extension](https://github.com/algolia/firestore-algolia-search/)\nprovides a synchronous hook to [call a user-supplied transform function](https://github.com/algolia/firestore-algolia-search/blob/34592d513eac22691d76917874a6466032976f67/functions/src/transform.ts)\nprior to writing to Algolia."]]