拡張機能にユーザーフックを追加する
コレクションでコンテンツを整理
必要に応じて、コンテンツの保存と分類を行います。
拡張機能をインストールするユーザーは、拡張機能の実行に独自のカスタム ロジックを挿入できます。これを行うには、次の 2 つの方法があります。
Eventarc イベント: イベントに非同期で対応するには、イベントを Eventarc に公開します。長時間実行タスクの完了後に通知を送信するイベント ハンドラ関数をデプロイしたり、独自の後処理関数を定義できます。
同期フック: 拡張機能にブロッキング ロジックを追加するには、拡張機能の動作で事前定義されたポイントに同期フックを追加します。そのポイントでユーザー提供の関数を実行し、その完了後に処理を続行できます。前処理タスクの多くはこのカテゴリに分類されます。
拡張機能では、このいずれかまたは両方の方法を使用できます。
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、拡張機能の名前、イベント名のフィールドは必須です。バージョンのフィールドは推奨です。公開するイベントタイプごとに、一意でわかりやすいイベント名を選択します。
たとえば、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 に公開することで非同期フックを提供します。
同期フック
拡張機能の関数の 1 つを機能させるために正常に完了する必要があるフックをユーザーに提供する場合は、同期フックを使用します。
同期フックは、ユーザー定義の HTTPS 呼び出し可能 Cloud Functions の関数を呼び出し、その完了を待ってから次に進みます(値が返される場合もあります)。ユーザーが指定した関数がエラーになると、拡張機能の関数もエラーになります。
同期フックを公開するには:
ユーザーが Cloud Functions のカスタム関数の URL を使用して拡張機能を構成するパラメータを拡張機能に追加します。次に例を示します。
- 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
フックを公開する拡張機能のポイントで、URL を使用して関数を呼び出します。次に例を示します。
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 に書き込む前にユーザー提供の変換関数を呼び出す同期フックを提供します。
特に記載のない限り、このページのコンテンツはクリエイティブ・コモンズの表示 4.0 ライセンスにより使用許諾されます。コードサンプルは Apache 2.0 ライセンスにより使用許諾されます。詳しくは、Google Developers サイトのポリシーをご覧ください。Java は Oracle および関連会社の登録商標です。
最終更新日 2025-08-29 UTC。
[null,null,["最終更新日 2025-08-29 UTC。"],[],[],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."]]