Node.js-Funktionen der 1. Generation auf die 2. Generation umstellen

Apps, die Funktionen der 1. Generation verwenden, sollten gemäß den Anweisungen in diesem Leitfaden zu Funktionen der 2. Generation migriert werden. Funktionen der 2. Generation verwenden Cloud Run, um eine bessere Leistung, eine bessere Konfiguration, eine bessere Überwachung und vieles mehr zu bieten.

In den Beispielen in diesem Dokument wird davon ausgegangen, dass Sie JavaScript mit CommonJS-Modulen (require-Importe) verwenden. Dieselben Prinzipien gelten jedoch auch für JavaScript mit ESM (import … from-Importe) und TypeScript.

Der Migrationsvorgang

Funktionen der 1. Generation und 2. Generation können in derselben Datei nebeneinander vorhanden sein. So können Sie Ihre Codebasis nach und nach migrieren, wenn Sie bereit sind. Wir empfehlen, jeweils eine Funktion zu migrieren und Tests und Überprüfungen durchzuführen, bevor Sie fortfahren.

Versionen von Firebase CLI und firebase-functions prüfen

Achten Sie darauf, dass Sie mindestens die Firebase CLI-Version 12.00 und firebase-functions Version 4.3.0 verwenden. Neuere Versionen unterstützen sowohl die 2. als auch die 1. Generation.

Importe aktualisieren

Funktionen der 2. Generation werden aus dem Unterpaket v2 im firebase-functions-SDK importiert. Dieser andere Importpfad ist alles, was die Firebase CLI benötigt, um zu bestimmen, ob Ihr Funktionscode als Funktion der 1. oder 2. Generation bereitgestellt werden soll.

Das Unterpaket v2 ist modular. Wir empfehlen, nur das spezifische Modul zu importieren, das Sie benötigen.

Vorher: 1. Generation

const functions = require("firebase-functions/v1");

Nachher: 2. Generation

// explicitly import each trigger
const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");

Triggerdefinitionen aktualisieren

Da das SDK der 2. Generation modulare Importe bevorzugt, müssen Sie die Triggerdefinitionen aktualisieren, um die geänderten Importe aus dem vorherigen Schritt zu berücksichtigen.

Die Argumente, die an Callbacks für einige Trigger übergeben werden, haben sich geändert. In diesem Beispiel wurden die Argumente für den onDocumentCreated-Callback in einem einzelnen event-Objekt zusammengefasst. Außerdem bieten einige Trigger praktische neue Konfigurationsfunktionen, z. B. die Option cors des onRequest-Triggers.

Vorher: 1. Generation

const functions = require("firebase-functions/v1");

exports.date = functions.https.onRequest((req, res) => {
  // ...
});

exports.uppercase = functions.firestore
  .document("my-collection/{docId}")
  .onCreate((change, context) => {
    // ...
  });

Nachher: 2. Generation

const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");

exports.date = onRequest({cors: true}, (req, res) => {
  // ...
});

exports.uppercase = onDocumentCreated("my-collection/{docId}", (event) => {
  /* ... */
});

Aufwand für das Umschreiben mit JavaScript-Destrukturierung minimieren

Wenn Ihre Funktionen komplexe Bodies haben, die stark von Kontext der 1. Generation oder anbieterspezifischen Parametern (z. B. message oder snapshot) abhängen, können Sie die Kompatibilitätshilfen der 1. Generation verwenden, die in das SDK der 2. Generation integriert sind.

Das SDK der 2. Generation patcht das Ereignisobjekt automatisch mit Gettern, die mit den Signaturen der 1. Generation übereinstimmen. So können Sie die JavaScript-Destrukturierung verwenden, um diese Eigenschaften direkt in der Handler-Signatur zu extrahieren. Dadurch wird der Aufwand für das Umschreiben Ihrer Funktionslogik minimiert.

Referenz für die Anbieterzuordnung

Anbieter Argumente der 1. Generation Gepatchte Ereignisdestrukturierung der 2. Generation
Pub/Sub (message, context) ({ message, context }) => { ... }
Firestore (snapshot, context) ({ snapshot, context }) => { ... }
Speicher (object, context) ({ object, context }) => { ... }
Realtime Database (snapshot, context) ({ snapshot, context }) => { ... }
Remote Config (version, context) ({ version, context }) => { ... }
Planer (context) ({ context }) => { ... }
Aufgabenwarteschlange (data, context) ({ data, context }) => { ... }

Vorher (1. Generation) :

export const myPubSubV1 = functions.pubsub.topic("my-topic").onPublish((message, context) => {
  const data = message.json;
  const eventId = context.eventId;
  // ... rest of the logic
});

Neue Alternative (2. Generation mit Destrukturierung) :

import { onMessagePublished } from "firebase-functions/v2/pubsub";

export const myPubSubV2 = onMessagePublished("my-topic", ({ message, context }) => {
  // No need to change the function body!
  const data = message.json;      // Uses v1 Message wrapper
  const eventId = context.eventId; // Uses v1 EventContext map
  // ... rest of the logic
});

Parametrisierte Konfiguration verwenden

Funktionen der 2. Generation unterstützen functions.config nicht mehr. Stattdessen wird eine sicherere Schnittstelle verwendet, um Konfigurationsparameter deklarativ in der Codebasis zu definieren. Mit dem neuen Modul params blockiert die CLI die Bereitstellung, es sei denn, alle Parameter haben einen gültigen Wert. So wird sichergestellt, dass eine Funktion nicht mit fehlender Konfiguration bereitgestellt wird.

Vorher: 1. Generation

const functions = require("firebase-functions/v1");

exports.getQuote = functions.https.onRequest(async (req, res) => {
  const quote = await fetchMotivationalQuote(functions.config().apiKey);
  // ...
});

Nachher: 2. Generation

const {onRequest} = require("firebase-functions/v2/https");
const {defineSecret} = require("firebase-functions/params");

// Define the secret parameter
const apiKey = defineSecret("API_KEY");

exports.getQuote = onRequest(
  // make the secret available to this function
  { secrets: [apiKey] },
  async (req, res) => {
    // retrieve the value of the secret
    const quote = await fetchMotivationalQuote(apiKey.value());
    // ...
  }
);

Wenn Sie eine vorhandene Umgebungskonfiguration mit functions.config haben, migrieren Sie diese Konfiguration im Rahmen des Upgrades auf die 2. Generation.

Die functions.config-API ist veraltet und wird im März 2027 eingestellt. Nach diesem Datum schlagen Bereitstellungen mit functions.config fehl.

Um Bereitstellungsfehler zu vermeiden, migrieren Sie Ihre Konfiguration mit der Firebase CLI zu Cloud Secret Manager. Dies ist die effizienteste und sicherste Methode zum Migrieren Ihrer Konfiguration.

  1. Konfiguration mit der Firebase CLI exportieren

    Verwenden Sie den Befehl config export, um Ihre vorhandene Umgebungskonfiguration in ein neues Secret in Cloud Secret Manager zu exportieren:

    $ firebase functions:config:export
    i  This command retrieves your Runtime Config values (accessed via functions.config())
       and exports them as a Secret Manager secret.
    
    i  Fetching your existing functions.config() from your project...     Fetched your existing functions.config().
    
    i  Configuration to be exported:
    ⚠  This may contain sensitive data. Do not share this output.
    
    {
       ...
    } What would you like to name the new secret for your configuration? RUNTIME_CONFIG
    
    ✔  Created new secret version projects/project/secrets/RUNTIME_CONFIG/versions/1```
    
  2. Funktionscode aktualisieren, um Secrets zu binden

    Wenn Sie die im neuen Secret in Cloud Secret Manager gespeicherte Konfiguration verwenden möchten, verwenden Sie die defineJsonSecret-API im Quellcode Ihrer Funktion. Achten Sie außerdem darauf, dass Secrets an alle Funktionen gebunden sind, die sie benötigen.

    Vorher

    const functions = require("firebase-functions/v1");
    
    exports.myFunction = functions.https.onRequest((req, res) => {
      const apiKey = functions.config().someapi.key;
      // ...
    });
    

    Nachher

    const { onRequest } = require("firebase-functions/v2/https");
    const { defineJsonSecret } = require("firebase-functions/params");
    
    const config = defineJsonSecret("RUNTIME_CONFIG");
    
    exports.myFunction = onRequest(
      // Bind secret to your function
      { secrets: [config] },
      (req, res) => {
        // Access secret values via .value()
        const apiKey = config.value().someapi.key;
        // ...
    });
    
  3. Funktionen bereitstellen

    Stellen Sie Ihre aktualisierten Funktionen bereit, um die Änderungen zu übernehmen und die Secret-Berechtigungen zu binden.

    firebase deploy --only functions:<your-function-name>
    

Laufzeitoptionen festlegen

Die Konfiguration der Laufzeitoptionen hat sich zwischen der 1. und der 2. Generation geändert. Mit der 2. Generation wird auch eine neue Funktion hinzugefügt, mit der Optionen für alle Funktionen festgelegt werden können.

Vorher: 1. Generation

const functions = require("firebase-functions/v1");

exports.date = functions
  .runWith({
    // Keep 5 instances warm for this latency-critical function
    minInstances: 5,
  })
  // locate function closest to users
  .region("asia-northeast1")
  .https.onRequest((req, res) => {
    // ...
  });

exports.uppercase = functions
  // locate function closest to users and database
  .region("asia-northeast1")
  .firestore.document("my-collection/{docId}")
  .onCreate((change, context) => {
    // ...
  });

Nachher: 2. Generation

const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");
const {setGlobalOptions} = require("firebase-functions/v2");

// locate all functions closest to users
setGlobalOptions({ region: "asia-northeast1" });

exports.date = onRequest({
    // Keep 5 instances warm for this latency-critical function
    minInstances: 5,
  }, (req, res) => {
  // ...
});

exports.uppercase = onDocumentCreated("my-collection/{docId}", (event) => {
  /* ... */
});

Standarddienstkonto aktualisieren (optional)

Während Funktionen der 1. Generation das Google App Engine-Standarddienstkonto verwenden, um den Zugriff auf Firebase APIs zu autorisieren, verwenden Funktionen der 2. Generation das Compute Engine-Standarddienstkonto. Dieser Unterschied kann zu Berechtigungsproblemen für Funktionen führen, die zur 2. Generation migriert wurden, wenn Sie dem Dienstkonto der 1. Generation spezielle Berechtigungen gewährt haben. Wenn Sie keine Berechtigungen für Dienstkonten geändert haben, können Sie diesen Schritt überspringen.

Die empfohlene Lösung besteht darin, das vorhandene App Engine-Standarddienstkonto der 1. Generation explizit Funktionen zuzuweisen, die Sie zur 2. Generation migrieren möchten, und so die Standardeinstellung der 2. Generation zu überschreiben. Dazu müssen Sie sicherstellen, dass für jede migrierte Funktion der richtige Wert für serviceAccountEmail festgelegt ist:

const {onRequest} = require("firebase-functions/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");
const {setGlobalOptions} = require("firebase-functions");

// Use the App Engine default service account for all functions
setGlobalOptions({serviceAccountEmail: '<my-project-number>@<wbr>appspot.gserviceaccount.com'});

// Now I use the App Engine default service account.
exports.date = onRequest({cors: true}, (req, res) => {
  // ...
});

// I do too!
exports.uppercase = onDocumentCreated("my-collection/{docId}", (event) => {
  // ...
});

Alternativ können Sie die Details des Dienstkontos so ändern, dass sie alle erforderlichen Berechtigungen sowohl für das App Engine-Standarddienstkonto (für die 1. Generation) als auch für das Compute Engine-Standarddienstkonto (für die 2. Generation) enthalten.

Nebenläufigkeit verwenden

Ein wesentlicher Vorteil von Funktionen der 2. Generation ist, dass eine einzelne Funktionsinstanz mehrere Anfragen gleichzeitig verarbeiten kann. Dadurch kann die Anzahl der Kaltstarts für Endnutzer erheblich reduziert werden. Standardmäßig ist die Gleichzeitigkeit auf 80 festgelegt. Sie können sie jedoch auf einen beliebigen Wert zwischen 1 und 1.000 festlegen:

const {onRequest} = require("firebase-functions/v2/https");

exports.date = onRequest({
    // set concurrency value
    concurrency: 500
  },
  (req, res) => {
    // ...
});

Durch Optimieren der Gleichzeitigkeit können Sie die Leistung verbessern und die Kosten für Funktionen senken. Weitere Informationen zur Nebenläufigkeit finden Sie unter Gleichzeitige Anfragen zulassen.

Verwendung globaler Variablen prüfen

Funktionen der 1. Generation, die ohne Berücksichtigung der Nebenläufigkeit geschrieben wurden, verwenden möglicherweise globale Variablen, die bei jeder Anfrage festgelegt und gelesen werden. Wenn die Nebenläufigkeit aktiviert ist und eine einzelne Instanz mehrere Anfragen gleichzeitig verarbeitet, kann dies zu Fehlern in Ihrer Funktion führen, da gleichzeitige Anfragen globale Variablen gleichzeitig festlegen und lesen.

Beim Upgrade können Sie die CPU Ihrer Funktion auf gcf_gen1 und concurrency auf 1 festlegen, um das Verhalten der 1. Generation wiederherzustellen:

const {onRequest} = require("firebase-functions/v2/https");

exports.date = onRequest({
    // TEMPORARY FIX: remove concurrency
    cpu: "gcf_gen1",
    concurrency: 1
  },
  (req, res) => {
    // ...
});

Dies wird jedoch nicht als langfristige Lösung empfohlen, da die Leistungsvorteile von Funktionen der 2. Generation verloren gehen. Prüfen Sie stattdessen die Verwendung globaler Variablen in Ihren Funktionen und entfernen Sie diese temporären Einstellungen, wenn Sie bereit sind.

Traffic zu den neuen Funktionen der 2. Generation migrieren

Wie beim Ändern der Region oder des Triggertyps einer Funktion, müssen Sie der Funktion der 2. Generation einen neuen Namen geben und den Traffic langsam dorthin migrieren.

Es ist nicht möglich, ein Upgrade einer Funktion von der 1. zur 2. Generation mit demselben Namen durchzuführen und firebase deploy auszuführen. In diesem Fall wird der folgende Fehler ausgegeben:

Upgrading from GCFv1 to GCFv2 is not yet supported. Please delete your old function or wait for this feature to be ready.

Die Migrationsstrategie hängt vom Triggertyp ab, den Ihre Funktion verwendet.

Für aufrufbare Funktionen, Aufgabenwarteschlangen und HTTP-Trigger

Diese Trigger sind direkte Aufrufe. Da die Funktion der 2. Generation einen neuen Namen (und eine neue URL für HTTP-Trigger) hat, können Sie den Traffic migrieren, indem Sie die Clients aktualisieren.

  1. Benennen Sie die Funktion in Ihrem Code um (z.B. von myCallable in myCallableV2).
  2. Stellen Sie die Funktion bereit. Sowohl die Funktion der 1. als auch die der 2. Generation werden jetzt ausgeführt.
  3. Aktualisieren Sie Ihren Clientcode oder Aufrufer, um auf den Namen oder die URL der neuen Funktion der 2. Generation zu verweisen.
  4. Sobald der gesamte Traffic zur neuen Funktion verschoben wurde, löschen Sie die Funktion der 1. Generation mit dem Befehl firebase functions:delete der Firebase CLI.

Für Hintergrundtrigger (Pub/Sub, Cloud Firestore, Cloud Storage usw.)

Hintergrundtrigger reagieren auf Ereignisse in Ihrem Projekt. Um zu vermeiden, dass während der Umstellung Ereignisse verloren gehen, müssen Sie die Funktionen der 1. und 2. Generation vorübergehend nebeneinander ausführen.

Während der Übergangszeit werden beide Funktionen durch dasselbe Ereignis ausgelöst. Das bedeutet, dass Ihre Geschäftslogik zweimal pro Ereignis ausgeführt wird. Achten Sie darauf, dass Ihre Funktion idempotent ist, bevor Sie fortfahren.

Schritt 1: Funktion der 2. Generation neben der Funktion der 1. Generation hinzufügen

Behalten Sie die vorhandene Funktion der 1. Generation in Ihrem Code bei und fügen Sie die Funktion der 2. Generation hinzu, die auf dieselbe Ereignisquelle wartet.

Tipp: Passthrough zur Überprüfung verwenden Um zu vermeiden, dass Sie Ihre Geschäftslogik während der Umstellung in Ihrer Codebasis duplizieren, oder um zu prüfen, ob die Funktion der 2. Generation Ereignisse korrekt empfängt, bevor Sie ihr vollständig vertrauen, können Sie die Funktion der 2. Generation als Passthrough verwenden, der die Funktion der 1. Generation mit der run Methode aufruft.

import * as functions from "firebase-functions/v1";
import { onMessagePublished } from "firebase-functions/v2/pubsub";

// --- Existing 1st gen function ---
export const myPubSub = functions.pubsub.topic("my-topic").onPublish((message, context) => {
  console.log("V1 handler running for event:", context.eventId);
  // ... existing v1 function logic ...
});

// --- New v2 passthrough function ---
export const myPubSubV2 = onMessagePublished("my-topic", async ({ message, context }) => {
   console.log("v2 handler triggering V1 for event:", context.eventId);
   // Call the v1 function's handler
   await myPubSub.run(message, context);
});

Schritt 2: Beide Funktionen bereitstellen

Führen Sie firebase deploy aus. Beide Funktionen sind jetzt aktiv und warten auf dieselben Ereignisse.

Schritt 3: Prüfen, ob die Funktion der 2. Generation Traffic empfängt

Beobachten Sie die Logs für beide Funktionen. Achten Sie darauf, dass die Funktion der 2. Generation für alle Ereignisse aufgerufen wird und dass die Aufrufe erfolgreich sind.

Schritt 4: Vollständige Logik zur Funktion der 2. Generation verschieben

Wenn Sie sich sicher sind, verschieben Sie die eigentliche Geschäftslogik von der Funktion der 1. Generation in den Body der Funktion der 2. Generation. Wenn Sie die Passthrough-Methode verwendet haben, entfernen Sie den Aufruf von myPubSub.run().

import * as functions from "firebase-functions/v1";
import { onMessagePublished } from "firebase-functions/v2/pubsub";

// --- Existing v1 function (to be removed next) ---
export const myPubSub = functions.pubsub.topic("my-topic").onPublish((message, context) => {
  console.log("v1 handler running for event:", context.eventId);
  // ... existing v1 function logic ...
});

// --- New v2 function with full logic ---
export const myPubSubV2 = onMessagePublished("my-topic", ({ message, context }) => {
   console.log("v2 handler running for event:", context.eventId);
   // ... existing v1 function logic WAS MOVED HERE ...
});

Stellen Sie diese Änderung bereit.

Schritt 5: Bereitstellung der Funktion der 1. Generation aufheben

Entfernen Sie die Definition der Funktion der 1. Generation aus Ihrem Code und stellen Sie sie noch einmal bereit. Die CLI wird Sie auffordern, die Funktion der 1. Generation aus Google Cloud zu löschen.

import { onMessagePublished } from "firebase-functions/v2/pubsub";

// --- V1 function definition REMOVED ---

// --- New v2 function with full logic ---
export const myPubSubV2 = onMessagePublished("my-topic", ({ message, context }) => {
   console.log("v2 handler running for event:", context.eventId);
   // ... existing v1 function logic ...
});