Firebase Extensions zu Cloud Functions migrieren

In diesem Leitfaden wird beschrieben, wie Sie Ihre Erweiterungen von der verworfenen Firebase Extensions-Umgebung zu einer Funktion migrieren, die Ihre Nutzer in ihrer eigenen Cloud Functions für Firebase (2. Generation)-Codebasis installieren und bereitstellen.

Dies ist der empfohlene Migrationspfad. Firebase führt eine Liste von Erweiterungen mit offiziellen npm-Äquivalenten. In diesem Leitfaden wird beschrieben, wie Sie Ihre eigene Erweiterung erstellen.

In diesem Leitfaden wird die Erweiterung Stream Cloud Firestore to BigQuery (firestore-bigquery-export) als laufendes Beispiel verwendet. Jeder Abschnitt endet mit einem Beispiel, das zeigt, wie die Erweiterung vor der Migration und wie sie danach als @firebase-function-kits/firestore-bigquery-export-Paket aussieht.

Registrieren Sie sich, um weitere Informationen und Hilfe zur Migration von Erweiterungen zu erhalten

Wenn Sie Fragen zur Migration von Firebase Extensions haben, können Sie uns unter firebase-extensions-migrator-support-external@google.com erreichen. Wir senden auch eine E-Mail an diese Gruppe, wenn wir den Leitfaden mit weiteren Informationen zum Verpacken, Testen und Verteilen Ihrer Funktionen der 2. Generation aktualisieren.

Wenn Sie dieser Gruppe beitreten möchten, senden Sie eine Nachricht an firebase-extensions-migrator-support-external+subscribe@google.com. Sie erhalten dann eine E-Mail mit einer Mitgliedschaftsanfrage. Sie müssen auf diese E-Mail antworten und dürfen nicht auf die Schaltfläche „Dieser Gruppe beitreten“ klicken.

Hinweis

Für diese Migration verwenden Sie die folgenden Funktionen von Cloud Functions:

  • Parametrisierte Konfiguration. Jeder Parameter, den Sie in extension.yaml deklarieren, wird zu einem definierten Parameter in Ihrem Paketcode.

  • Deklarative IAM-Rollen und erforderliche APIs. Jede Rolle, die Sie in extension.yaml deklarieren, wird zu einem requiresRole(...)-Aufruf und jede API zu einem requiresAPI(...)-Aufruf in Ihrem Paketcode. Bei der Bereitstellung weist die Firebase-Befehlszeile die deklarierten Rollen einem verwalteten Laufzeitdienstkonto zu und aktiviert die deklarierten APIs in Ihrem Namen.

  • Lifecycle-Ereignisse für Cloud Functions-Codebasen. Cloud Functions-Codebasen unterstützen jetzt Lifecycle-Events analog zu Firebase Extensions. Deklarieren Sie die Einrichtung bei der Installation und Aktualisierung mit den Lebenszyklus-Hooks afterFirstDeploy(...) und afterRedeploy(...). Diese ersetzen die lifecycleEvents, die Sie in extension.yaml deklarieren.

Firebase Extensions-Quelle zu einer Funktion der 2. Generation migrieren

(Optional) Automatische Migration mit dem Firebase-Agent-Skill

Sie können die Schritte 1 bis 8 (Ressourcen inventarisieren, Upgrades auslösen, Parameter- und Secret-Konvertierungen, deklaratives IAM, Lebenszyklus-Hooks und Generieren der README-Datei des Pakets) mit dem offiziellen KI-Agenten-Skill extension-to-functions-codebase automatisieren.

Skill installieren

Wenn Sie oder Ihr KI-Programmierassistent (Gemini in Firebase, Cursor, Claude Code, GitHub Copilot) den Skill noch nicht installiert haben, führen Sie den folgenden Befehl mit der Skills-CLI aus:

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

Sobald der Skill in Ihrem Projekt installiert ist, folgt Ihr KI-Programmierassistent automatisch den Migrationsregeln und Transformationsschritten. Sie können den folgenden Prompt verwenden:

„Bitte migrieren Sie diese Firebase-Erweiterung gemäß der Anleitung im Skill extension-to-functions-codebase in ein veröffentlichungsfähiges Function Kit-Paket der 2. Generation.“

1. Erweiterung inventarisieren

Erstellen Sie zuerst ein Inventar Ihrer Erweiterung: eine vollständige Liste aller Elemente, die die Erweiterung deklariert, bereitstellt und dokumentiert. So hat jedes Verhalten ein definiertes Ziel in der Funktion der 2. Generation und bei der Migration geht nichts verloren.

Prüfen Sie die folgenden Punkte und notieren Sie sich Ihre Ergebnisse:

  • extension.yaml, in der Ihre Parameter, Funktionen, Ereignisse, IAM-Rollen, erforderlichen APIs, Secrets und Lebenszyklus-Hooks deklariert werden.

  • functions/, das Ihren Funktionscode, Ihre Abhängigkeiten, Ihre Build-Konfiguration, Ihre Trigger und Ihre Aufgabenwarteschlangenfunktionen enthält.

  • README.md, PREINSTALL.md und POSTINSTALL.md, die Einrichtungsschritte, Warnungen und Abrechnungshinweise enthalten.

  • scripts/, das alle Import-, Backfill-, IAM-, Reparatur- oder Migrationsdienstprogramme sowie alle anderen Tools enthält, die Sie zusammen mit der Erweiterung bereitstellen.

Entscheiden Sie dann für jedes Element in extension.yaml, wo es im npm-Paket platziert werden soll:

  • Nutzerkonfiguration in Cloud Functions-Parameter umwandeln (Schritt 4).

  • Secrets in Cloud Functions-Secrets umwandeln (Schritt 4).

  • IAM-Rollen in requiresRole(...)-Deklarationen umwandeln (Schritt 6).

  • Konvertieren Sie erforderliche Google APIs gegebenenfalls in requiresAPI(...)-Deklarationen (Schritt 6).

  • Konvertieren Sie die Installations- und Aktualisierungs-Hooks in afterFirstDeploy(...)- und afterRedeploy(...)-Deklarationen (Schritt 7).

  • Instanz-IDs von EXT_INSTANCE_ID in FIREBASE_KIT_INSTANCE_ID konvertieren (Schritt 4).

Beispiel: Cloud Firestore an BigQuery streamen

Wenn Sie firestore-bigquery-export/extension.yaml und functions/ lesen, wird dieses Inventar erstellt:

In „extension.yaml“ Anzahl/Wert Nutzung
params 25 (COLLECTION_PATH, DATASET_ID, TABLE_ID, DATASET_LOCATION, VIEW_TYPE, …) Cloud Functions-Parameter (Schritt 4)
apis bigquery.googleapis.com requiresAPI(...) (Schritt 6)
roles bigquery.dataEditor, datastore.user, bigquery.user requiresRole(...) (Schritt 6)
resources 1 Ereignistrigger (fsexportbigquery) + Aufgabenwarteschlangen-Funktionen (initBigQuerySync, setupBigQuerySync) Exportierte Paketfunktionen (Schritt 3)
lifecycleEvents onInstall → initBigQuerySync; onUpdate / onConfigure → setupBigQuerySync afterFirstDeploy/afterRedeploy (Schritt 7)
Instanz-ID Nicht verwendet (keine EXT_INSTANCE_ID-Lesevorgänge) Keine Daten zum Migrieren vorhanden
scripts/ import/ (Backfill), gen-schema-view/ Als Skripts beibehalten (hier nicht relevant)

Analyse: Die Erweiterung deklariert keine type: secret-Parameter, sodass in Schritt 4 keine Secrets migriert werden müssen. Der Ereignis-Trigger ist bereits in der 2. Generation. Nur die Aufgabenwarteschlangenfunktionen sind noch in der 1. Generation (relevant in Schritt 3).

2. package.json aktualisieren

Aktualisieren Sie die Datei package.json Ihrer Erweiterung. Wenn Sie eine Erweiterung migrieren, kann dies das Stammverzeichnis package.json sein. Wenn Sie viele Erweiterungen in einem Repository migrieren, weisen Sie jeder Erweiterung ein eigenes Paket zu.

Mindestens erforderliche SDK-Versionen: Deklarieren Sie firebase-functions >= 7.4.0 und firebase-admin >= 14.2.0 als Abhängigkeiten. Deklarieren Sie Ihre Version von firebase-functions auch als Peer-Abhängigkeit, damit das Cloud Functions-Projekt Ihrer Nutzer dieselbe Version des SDK hat, mit der Ihre Bibliothek geschrieben wurde.

{
  "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"
  }
}

Beispiel: Cloud Firestore an BigQuery streamen

Vorher Die functions/package.json der Erweiterung ist privat, nennt die Erweiterungs-ID und deklariert firebase-functions als direkte Abhängigkeit:

{
  "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"
  }
}

Nachher Ein veröffentlichbares Paket: Bereichsname, eine exports-Zuordnung und firebase-functions nach peerDependencies verschoben:

{
  "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. Funktionen von der 1. Generation auf die 2. Generation aktualisieren

Wenn Ihre Erweiterung weiterhin Funktionen der 1. Generation exportiert, konvertieren Sie jeden Trigger in das entsprechende Äquivalent der 2. Generation. Importieren Sie aus den firebase-functions/...-Modulen und übergeben Sie Laufzeiteinstellungen in den Triggeroptionen.

Weitere Informationen finden Sie im Upgrade-Leitfaden für Cloud Functions der 2. Generation. Insbesondere können Sie den Aufwand für das Umschreiben minimieren, indem Sie die gepatchte Ereignisdestrukturierung der 2. Generation verwenden und das Umschreiben Ihrer Funktionslogik vermeiden, da das SDK der 2. Generation die v1-Parameter als Felder im Ereignisobjekt verfügbar macht. So können Sie destrukturierte/benannte Parameter verwenden und Ihre Geschäftslogik unverändert lassen.

Vorher 1. Generation:

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);
  });

Nachher 2. Generation:

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

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

Eine umfassende Liste der Unterschiede zwischen Funktionen der 1. Generation und der 2. Generation finden Sie im Versionsvergleich für Cloud Functions.

Ein wesentlicher Unterschied zwischen Funktionen der 1. und 2. Generation besteht darin, dass Funktionen der 2. Generation am selben Ort wie ihre Triggerressourcen gehostet werden müssen. Wenn Nutzer von einer Erweiterung mit Funktionen der 1. Generation zu einem Kit mit Funktionen der 2. Generation migrieren, müssen sie möglicherweise die Funktionsstandorte ändern, um diese Anforderung zu erfüllen.

Alle von Ihren Nutzern ausgewählten Cloud Functions-Standorte können während der Migration beschädigt werden, da der vorhandene Standort beibehalten wird. Wenn die ereignisgesteuerten Funktionen Ihrer Erweiterung auf dem vom Nutzer angegebenen Funktionsstandort basieren, empfehlen wir, Ihrer Erweiterung einen neuen Parameter hinzuzufügen, um den Standort des Ereignisauslösers zu erfassen und dem neuen Funktionsstandort zuzuordnen.

Beispiel: Cloud Firestore an BigQuery streamen

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())
);

Bei der ersten Bereitstellung des Kits werden Nutzer nach ihrer DATABASE_REGION gefragt und die Funktionen werden in der entsprechenden functionRegion oben bereitgestellt, um alle Standortprobleme für ihre ereignisgesteuerten Funktionen der 2. Generation zu beheben.

4. Erweiterungsparameter und Secrets konvertieren

Parameter

Jeder Parameter, den Sie in extension.yaml deklarieren, wird zu einem Cloud Functions-Parameter.

Direkte Umgebungsmesswerte umrechnen:

const collectionPath = process.env.COLLECTION_PATH;

in Cloud Functions-Parameter:

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);
  }
);

Verwenden Sie collectionPath.value(), um den String in einem Handler zu lesen. Verwenden Sie collectionPath direkt dort, wo ein Platzhalter erwartet wird, z. B. in einem Triggerpfad für eine Funktion.

Die Firebase-Befehlszeile erkennt Ihre Parameter und liest ihre Werte aus .env, .env.<projectId> oder fordert Ihre Nutzer während der Bereitstellung auf, sie einzugeben. Behalten Sie die Parameternamen bei, damit die Werte aus einer vorhandenen Installation übernommen werden.

Es ist wichtig, dass Sie die in Ihrem Code deklarierten Parameternamen überhaupt nicht ändern. Bei der Erweiterungsmigration werden vorhandene Endnutzer-Parameterwerte automatisch beibehalten, aber nur, wenn die Namen unverändert sind.

Beispiel: Cloud Firestore an BigQuery streamen

Vorher Ein in extension.yaml deklarierter Parameter, der als Rohumgebungsvariable in config.ts gelesen wird:

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

Nachher Eine defineString; die Befehlszeile erkennt sie und liest aus .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 } }
}),

Der Parametername bleibt unverändert, sodass ein vorhandener .env weiterhin funktioniert.

Instanz-ID

Erweiterungen lesen ihre Instanz-ID aus EXT_INSTANCE_ID, die von der Erweiterungslaufzeit eingefügt wird. Funktions-Kits lesen ihre Instanz-ID aus FIREBASE_KIT_INSTANCE_ID, die die Firebase-Befehlszeile für jede Kit-Instanz auf den Schlüssel der Instanz in der instances-Zuordnung in firebase.json setzt. Die Befehlszeile stellt sie während der Erkennung bei der Bereitstellung, im Emulator und für die bereitgestellten Funktionen bereit.

Die Instanz-ID ist kein Parameter. Deklarieren Sie sie daher nicht mit defineString. Tatsächlich ist FIREBASE_... ein reserviertes Präfix in .env-Dateien, sodass Nutzer es dort nicht festlegen oder überschreiben können. Die von der Befehlszeile eingefügten Werte sind für das params-System nicht sichtbar. Lesen Sie sie direkt aus der Umgebung:

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

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

Die Variable wird nur festgelegt, wenn Ihr Paket als Kit bereitgestellt wird. Wenn Ihr Code auch als eigenständige Codebasis bereitgestellt wird (siehe Schritt 9), behandeln Sie ihn entweder als optional oder brechen Sie den Vorgang mit einer klaren Fehlermeldung ab, wenn er fehlt. Wenn Ihre Erweiterung die Instanz-ID als für Nutzer sichtbaren Parameter verfügbar gemacht hat, müssen Sie diesen Parameter entfernen, da die Firebase-Befehlszeile den Wert jetzt besitzt.

Beispiel: Nutzerdaten löschen

(Die Erweiterung „Stream Cloud Firestore to BigQuery“ liest ihre Instanz-ID nicht, daher gibt es dort nichts zu migrieren. Die Erweiterung „Delete User Data“ verwendet sie, um ihre Pub/Sub-Themen zu benennen.)

Vorher Als Rohumgebungsvariable in config.ts mit dem Präfix ext- lesen, das Erweiterungen für ihre Ressourcen verwendet haben:

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

Nachher Ein einfacher process.env-Lesevorgang von FIREBASE_KIT_INSTANCE_ID, der für zwei normale Parameter verwendet wird, damit Nutzer die Themennamen überschreiben können:

// 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`,
}),

Die Standardwerte dürfen nicht leer sein, da Triggerbindungen bei der Erkennung aufgelöst werden. Ein leerer Standardwert wird als Themenname in das Bereitstellungsmanifest geschrieben. Das Kit ist auch gegen die Ausführung außerhalb des Kontexts eines Kits geschützt. Wenn die Variable fehlt, wird der Standardwert auf Modulebene als kit-undefined-discovery ausgewertet, sodass der Konfigurations-Loader stattdessen mit einem erklärenden Fehler fehlschlägt:

// ...
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."
  );
}
// ...

Diese Prüfung wird ausgeführt, wenn ein Handler seine Konfiguration zum ersten Mal auflöst. Eine fehlende Variable führt also zu einem klaren Laufzeitfehler, anstatt dass Funktionen stillschweigend an kit-undefined-*-Themen gebunden werden. Wenn Sie die Bereitstellung selbst ablehnen möchten, führen Sie die Prüfung im Modulbereich durch, damit sie während der Erkennung ausgeführt wird. Da die CLI die Instanz-ID von firebase.json ableitet, gibt es keine konfigurierbare INSTANCE_ID und nichts, was über mehrere Instanzen hinweg synchronisiert werden muss.

Secrets

In extension.yaml deklarieren Sie Secrets mit type: secret. Die Erweiterungslaufzeit speichert und bindet sie, sodass der Erweiterungscode process.env.PARAM_NAME direkt lesen kann. In einer typischen Cloud Functions-Codebasis deklarieren und binden Sie jedes Secret explizit:

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

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

Sobald Ihre Erweiterung zu einem npm-Paket/Kit migriert wurde, werden Secret-Referenzen in der .env-Datei des Endnutzers verwaltet. Es ist wichtig, dass Sie die in Ihrem Code deklarierten Secret-Namen überhaupt nicht ändern. Während der Migration werden die Secrets der Endnutzer entsprechend migriert.

Beispiel: E‑Mail von Cloud Firestore auslösen

Vorher MAIL_COLLECTION und SMTP_PASSWORD werden als Rohumgebungsvariablen in config.ts gelesen:

# 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,

Nachher Eine defineString und eine defineSecret; die CLI erkennt beide und liest aus .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. Interne Aufgabenwarteschlangenaufrufe migrieren

Einige Erweiterungen stellen Aufgaben in ihren eigenen Aufgabenwarteschlangen aus ihrem Funktionscode heraus mit dem Firebase Admin SDK in die Warteschlange. Dies unterscheidet sich vom Empfangen einer gesendeten Aufgabe (siehe Abschnitte Funktionen aktualisieren und Lebenszyklus-Hooks konvertieren). In diesem Fall ist Ihr Code der Producer, der queue.enqueue(...) aufruft.

In früheren Versionen von Admin SDK mussten Erweiterungen ihre eigene Erweiterungsinstanz-ID als zweiten Parameter übergeben, um eine Task Queue-Funktion in derselben Erweiterung anzusprechen. Ab firebase-admin 14.2.0 ist dies weder erforderlich noch empfohlen. Die Task Queue API ist jetzt standardmäßig auf Aufgabenwarteschlangen im selben Kontext (z. B. eine Erweiterung oder ein Kit) ausgerichtet. Sie können diesen Parameter sowohl in Erweiterungen als auch in eigenständigen Funktionen bedenkenlos aus Ihrem Code entfernen. Wenn Sie diesen Parameter entfernen, werden Portabilität und Aufwärtskompatibilität gewährleistet.

Alles andere am Aufruf zum Einreihen in die Warteschlange – der Ressourcenpfad locations/<region>/functions/<name>, die Aufgabennutzlast und die Wiederholungslogik – bleibt gleich.

Weitere Informationen zum Einreihen von Funktionen mit Cloud Tasks finden Sie unter Funktionen mit Cloud Tasks in die Warteschlange stellen.

Vorher Erweiterung der 1. Generation:

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);

Nachher Verlängerung der 2. Generation:

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

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

Wenn Ihr Enqueue-Aufruf auf eine Codebasis mit Präfix abzielt, wird der erkannte Funktionsname ebenfalls mit einem Präfix versehen (z. B. orders-syncBigQuery). Weitere Informationen finden Sie unter Ersatz-Funktions-Kit-Instanz prüfen und installieren und Als Funktions-Kit testen.

6. Erforderliche APIs und IAM-Rollen deklarieren

Verschieben Sie die IAM- und API-Anforderungen Ihrer Erweiterung aus extension.yaml in den Code:

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

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

Bei der deklarativen Sicherheit erstellt oder aktualisiert die Firebase-CLI ein verwaltetes Laufzeitdienstkonto für die Codebasis und weist ihm die Vereinigung aller deklarierten Rollen zu. Dokumentieren Sie für Ihre Nutzer, dass alle Funktionen in der Codebasis mit diesen Rollen ausgeführt werden, es sei denn, die endgültige API unterstützt ein eingeschränkteres Modell.

Beispiel: Cloud Firestore an BigQuery streamen

Vorher In extension.yaml deklariert; die Erweiterungslaufzeit hat die API aktiviert und die Rollen einem verwalteten Konto zugewiesen:

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

Nachher Im Code mit requiresAPI und requiresRole deklariert:

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");

Wenn Ihre Erweiterung Eventarc-Ereignisse veröffentlicht, müssen Sie auch die entsprechenden erforderlichen Rollen und APIs für die Veröffentlichung von Ereignissen festlegen. Dies wurde zuvor von Erweiterungen verarbeitet, ohne dass Änderungen an Ihrer extensions.yaml erforderlich waren. Sie können dies in Ihrem Code bedingt tun, sodass das Kit diese Berechtigungen nur anfordert, wenn ein benutzerdefinierter Eventarc-Kanal verwendet wird.

if (!!process.env.EVENTARC_CHANNEL) {
  requiresRole("roles/eventarc.publisher");
  requiresAPI(
    "eventarcpublishing.googleapis.com",
    "Publishes the extension's custom events to its Eventarc channel."
  );
}

7. Lebenszyklus-Hooks konvertieren

Wenn Ihre Erweiterung getExtensions().runtime() aufruft (z. B. setProcessingState oder setFatalError), löschen Sie diese Aufrufe, da sie einen Fehler auslösen, wenn sie von einer normal bereitgestellten Funktion der 2. Generation aufgerufen werden. Der Lebenszyklusstatus wird jetzt von afterFirstDeploy und afterRedeploy gesteuert, wobei dieses Statustracking nicht verwendet wird.

Firebase Extensions kann die Einrichtung ausführen, wenn ein Nutzer eine Erweiterung installiert, aktualisiert oder neu konfiguriert. Deklarieren Sie in Ihrem npm-Paket entsprechende Lebenszyklusaktionen im Code.

Für die einmalige Einrichtung:

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: {}
  }
});

Für Konfigurations- oder Codeaktualisierungen:

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

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

Machen Sie Ihre Lebenszyklusaktionen idempotent. Ihre Nutzer müssen sie möglicherweise manuell noch einmal ausführen, wenn die Verteilung oder Ausführung fehlschlägt:

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

Beispiel: Cloud Firestore an BigQuery streamen

Vorher lifecycleEvents in extension.yaml, verursacht durch die Erweiterungslaufzeit:

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

Nachher Im Code deklariert; die Aufgabe stellt BigQuery bei der ersten Bereitstellung bereit:

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

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

Die Bereitstellung ist idempotent. Bei einer erneuten Ausführung werden das Dataset, die Tabelle und die Ansichten abgeglichen. Nutzer können die Ausführung mit firebase functions:lifecycle:run afterFirstDeploy CODEBASE_NAME manuell wiederholen.

8. Dokumenteinrichtung für Ihre Nutzer

Schreiben Sie ein Paket README, das mindestens Folgendes erklärt:

  • Die .env-Werte, die das Paket benötigt.
  • Die Secrets, die das Paket benötigt, und wie vorhandene Secret-Werte migriert werden.
  • Die IAM-Rollen, die das Paket mit requiresRole(...) deklariert.
  • Die Google APIs, die das Paket aktiviert oder benötigt.
  • Die vom Paket deklarierten Lebenszyklus-Hooks und wie sie manuell noch einmal ausgeführt werden.
  • Abrechnungshinweise.
  • Was sich im Vergleich zur ursprünglichen Erweiterung geändert hat.
  • Wie das Paket seine Instanz-ID abruft (FIREBASE_KIT_INSTANCE_ID, festgelegt durch die Befehlszeile) und dass alle Funktionen der Instanz mit dem Präfix kit-<instanceId>- bereitgestellt werden.

Beispiel: Cloud Firestore an BigQuery streamen

Das Paket README enthält eine konkrete Tabelle mit den Änderungen:

Bedenken Als Erweiterung Als @firebase-function-kits/firestore-bigquery-export
Konfiguration Erweiterungsparameter Cloud Functions Parameter über .env
IAM Durch Erweiterungen gewährt requiresRole(...), bei der Bereitstellung angewendet
Wird bereitgestellt Lebenszyklusaufgabe nach Erweiterungen afterFirstDeploy/afterRedeploy Aufgabe
Funktionsnamen ext-<instanceId>-fsexportbigquery fsexportbigquery (optional mit Präfix)
Instanz-ID EXT_INSTANCE_ID von Erweiterungen eingefügt FIREBASE_KIT_INSTANCE_ID, von der Befehlszeile aus firebase.json festgelegt

9. Funktion der 2. Generation testen

Sie sollten jetzt eine Funktion der 2. Generation haben, die sich nach der Bereitstellung genauso verhält wie eine neue Installation Ihrer Erweiterung. Im nächsten Schritt werden alle Probleme überprüft und behoben, die versehentlich aufgetreten sind.

Wenn Sie setGlobalOptions aufrufen möchten, um globale Optionen wie eine Standardregion oder CPU festzulegen, müssen Sie dies nur tun, wenn Sie Ihr Kit als eigenständige Funktion der 2. Generation bereitstellen. Wenn Ihr Kit als npm-Paket installiert ist, rufen Ihre Nutzer setGlobalOptions in ihrem Wrapping-Code auf, um diese Parameter zu konfigurieren. Wenn dies zweimal geschieht, erhalten sie Warnungen. Sie können diesen Aufruf schützen, indem Sie die Umgebungsvariable FIREBASE_KIT_INSTANCE_ID prüfen:

import { setGlobalOptions } from "firebase-functions";

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

Achten Sie darauf, dass Sie firebase-tools >= 15.32.0 verwenden, und stellen Sie die konvertierte Funktion der 2. Generation in einem Testprojekt mit den entsprechenden Ressourcen bereit, um ihr Verhalten zu testen. Wenn Sie bereits ein Testprojekt zum Testen Ihrer Erweiterung eingerichtet haben, führen Sie den folgenden Befehl aus:

firebase deploy --only functions

Füllen Sie den resultierenden Assistenten, der Sie zur Eingabe von Parameterwerten auffordert, genauso aus, wie Sie das Installationsformular in der Firebase-Konsole für die Erweiterung ausgefüllt hätten.

Beispiel: Cloud Firestore an BigQuery streamen

Wir prüfen die Synchronisierung von Cloud Firestore mit BigQuery durchgängig:

  1. Erstellen Sie auf der Seite Cloud Firestore der Firebase Console die Sammlung, die Sie als COLLECTION_PATH (users) festgelegt haben, falls sie noch nicht vorhanden ist.
  2. Erstellen Sie ein Dokument mit dem Namen bigquery-mirror-test, das beliebige Felder mit beliebigen Werten enthält.
  3. Fragen Sie auf der Seite BigQuery der Google Cloud Console die Tabelle mit dem Roh-Änderungsprotokoll ab. Es sollte eine einzelne Zeile enthalten, in der die Erstellung des Dokuments protokolliert wird:

    SELECT * FROM `PROJECT_ID.analytics.users_raw_changelog`
    
  4. Fragen Sie die neueste Ansicht ab, die das neueste Änderungsereignis für das einzige vorhandene Dokument (bigquery-mirror-test) zurückgeben sollte:

    SELECT * FROM `PROJECT_ID.analytics.users_raw_latest`
    
  5. Löschen Sie das Dokument bigquery-mirror-test in Cloud Firestore. Sie verschwindet aus der neuesten Ansicht und ein DELETE-Ereignis wird an die Tabelle mit dem Rohänderungsprotokoll angehängt.

    Sie können den vollständigen Verlauf eines einzelnen Dokuments mit folgendem Befehl prüfen:

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

Unterschiede beim Testen der Erweiterung:

  • Der Trigger wird als fsexportbigquery bereitgestellt (kein Präfix bei der Bereitstellung als typische Funktion der 2. Generation und nicht als Kit), nicht als ext-<instanceId>-fsexportbigquery. Suchen Sie im Dashboard und in den Logs von Cloud Functions nach diesem Namen.
  • Ihr Code wird jetzt in Firebase Local Emulator Suite als normale Funktionen ausgeführt. Sie können den Wert von Parametern, die im Emulator verwendet werden sollen, mit .env.local festlegen. Sie können Ihren Code auch mit dem firebase-functions-test SDK testen, wie unter Unittests von Cloud Functions beschrieben.
  • Die Bereitstellung wird nicht mehr von der Erweiterungslaufzeit gesteuert. Wenn die Änderungsprotokolltabelle nach der Bereitstellung fehlt, führen Sie die Einrichtungsaufgabe manuell noch einmal aus: firebase functions:lifecycle:run afterFirstDeploy CODEBASE_NAME. Die Aufgabe ist idempotent. Wenn Sie sie also noch einmal ausführen, werden das Dataset, die Tabelle und die Ansichten abgeglichen.
  • Die Parameterwerte stammen aus .env und nicht aus dem Installationsformular. Daher sind Wiederholungen von firebase deploy nicht interaktiv, sobald .env abgeschlossen ist.

10. Funktions-Kit auf npm veröffentlichen

Nachdem Sie die Konvertierung von der Erweiterung in eine Funktion der 2. Generation validiert haben, können Sie einen Releasekandidaten für End-to-End-Tests in npm veröffentlichen. Verwenden Sie dazu eine der folgenden Anleitungen:

Wenn Ihr Kit in npm veröffentlicht wurde, kann es mit firebase functions:kits:install installiert und als offizieller Ersatz für Ihre Erweiterung aufgeführt werden.

Wir empfehlen dringend, zuerst einen Releasekandidaten zu veröffentlichen. Kits werden nach Paketname und Version installiert. Mit einer Vorabveröffentlichung können Sie den tatsächlichen Installationsablauf in der Registry testen, ohne ein unfertiges Paket für Nutzer freizugeben, die mit dem Standard-Tag latest installieren.

Vor der Veröffentlichung:

  1. Wählen Sie einen Paketnamen aus. Sowohl Namen mit als auch ohne Bereichsangabe funktionieren (siehe die Anleitungen oben). Beachten Sie, dass Pakete mit Bereich standardmäßig privat sind. Übergeben Sie daher --access public.
  2. Baue Schiffe und sieh dir an, welche du hast. main und types verweisen auf Ihre kompilierte Ausgabe (lib/ in unserem Beispiel). Dieses Verzeichnis muss also in der veröffentlichten TAR-Datei enthalten sein. Verwenden Sie .npmignore oder eine files-Zulassungsliste und prüfen Sie das Ergebnis mit npm pack --dry-run. Ein prepublishOnly-Skript, das Ihren Build ausführt, verhindert die Veröffentlichung veralteter Ausgaben.

package.json Ergänzungen, die das Veröffentlichen standardmäßig sicher machen:

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

Das Feld "publishConfig": { "tag": "next" } sorgt dafür, dass ein einfacher npm publish niemals latest überschreibt.

Releasekandidaten erstellen:

So erhöhen Sie beispielsweise die Version lokal von 0.0.2-rc.3 auf 0.0.2-rc.4 (dies führt zu Commits und Tags in Git, wenn sich package.json im Repository-Stammverzeichnis befindet):

npm version prerelease --preid rc

So veröffentlichen Sie den Releasekandidaten und registrieren @your-org/your-kit@0.0.2-rc.4 auf npm unter dem Tag next:

npm publish

Es kann einige Minuten dauern, bis die neue Version auf der npm-Website angezeigt wird. npm view liest die Registry direkt:

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

Sobald Schritt 11 und Schritt 12 dieses Leitfadens abgeschlossen sind, können Sie das Paket auf eine stabile Version hochstufen:

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

Hinweis zu npm-shrinkwrap.json: Wir empfehlen dringend, eine npm-shrinkwrap.json-Datei in Ihr Paket aufzunehmen. Wenn Sie das nicht tun, werden Nutzer bei der Installation von der Befehlszeile gewarnt. So wird sichergestellt, dass Nutzer genau die Abhängigkeiten verwenden, die Sie getestet haben, und es hilft, sich vor Angriffen auf die Lieferkette zu schützen. Ein Shrinkwrap wird jedoch in den Projekten Ihrer Nutzer unverändert angewendet, auch während des Cloud Functions-Builds (npm ci), bei dem reine Entwicklungseinträge mit EBADPLATFORM fehlschlagen können. Möglicherweise müssen Sie "dev": true-Einträge und devDependencies aus der veröffentlichten Shrinkwrap-Kopie entfernen.

Beispiel: Cloud Firestore an BigQuery streamen

package.json des Kits zum Zeitpunkt des vierten Releasekandidaten:

{
  "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" }
}

Beachten Sie, dass sich das Kit in diesem Fall in einem Monorepo befindet. Daher ist es wichtig, repository.directory anzugeben, damit der npm-Registry-Link auf den richtigen Ordner verweist. Die Datei CHANGELOG.md enthält Hinweise zum ausstehenden Release.

11. Als Funktions-Kit testen

Nachdem Sie Ihr Kit veröffentlicht haben, empfehlen wir, es mit npm zu testen.

Prüfen Sie, ob Sie die firebase-tools-Version >= 15.32.0 verwenden, und installieren Sie das Kit:

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

Dadurch wird Ihr Paket von npm heruntergeladen, in einem neuen Quellverzeichnis für Ihr Kit eingerichtet und Sie werden durch die Konfiguration der ersten Instanz geführt, ähnlich wie bei der Installation von Erweiterungen. Nachdem Sie das Paket lokal installiert und eingerichtet haben, führen Sie eine Bereitstellung aus, um die Ressourcen in Ihrem Google Cloud-Projekt zu erstellen:

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

Nach der Installation gibt die Firebase-Befehlszeile einen ähnlichen Bereitstellungsbefehl mit der genauen Instanz-ID aus, die Sie bei der Installation ausgewählt haben.

Validieren Sie Ihr Kit noch einmal. Folgen Sie dazu der Anleitung in Schritt 9. Funktion der 2. Generation testen Da Sie jetzt mit Kits bereitstellen, werden Ihre Funktionen mit dem Präfix kit-<instance-id>-<method-name> versehen und benannt. So können Kits mehrere Instanzen haben und dieselbe Funktion mehrmals in einem Projekt bereitstellen, jeweils mit einem eindeutigen Namen.

12. Migrationsersatz testen

Sie können eine funktionierende Erweiterungsinstanz einrichten und dann die Migrationsanleitung für Nutzer (entweder mit firebase ext:migrate --package oder den Befehlen der Funktions-Kits-Befehlszeile) verwenden, um das Testen Ihres Funktions-Kits als Migrationsersatz abzuschließen.

13. Nutzer und Google über den offiziellen Ersatz für die Erweiterung informieren

Sobald Ihr Funktions-Kit-Ersatz bereit und als npm-Paket verfügbar ist, zu dem Nutzer migrieren sollten, informieren Sie sowohl Ihre Nutzer als auch Google über diesen offiziellen Ersatz. Aktualisieren Sie die Datei README.md im GitHub-Repository, in dem Ihre Erweiterung gehostet wird, mit den folgenden Informationen:

<!-- 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 scannt bekannte README-Dateien von Erweiterungen nach Kommentaren wie <!-- FIREBASE_EXTENSION_REPLACEMENT: extension="firebase/firestore-bigquery-export" package="@firebase-function-kits/firestore-bigquery-export" --> und verwendet diese, um unsere offizielle Registrierung von Ersetzungen zu füllen, die im Repository firebase-tools als replacements.json gespeichert sind. Sie können auch replacements.json prüfen, um zu sehen, welche README.md für Ihre Erweiterung gescannt werden. Die offizielle Liste der Ersatzteile wird wöchentlich aktualisiert.

Beispiel: Cloud Firestore an BigQuery streamen

Die firestore-bigquery-export-Erweiterung README.md enthält:

<!-- 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.