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

Apps, die Funktionen der 1. Generation verwenden, sollten gemäß der Anleitung in diesem Leitfaden zur 2. Generation migrieren. Funktionen der 2. Generation verwenden Cloud Run, um eine bessere Leistung, bessere Konfiguration, besseres Monitoring und 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. und 2. Generation können in derselben Quelldatei nebeneinander vorhanden sein. So können Sie Ihre Codebasis nach und nach migrieren, wenn Sie bereit sind. Beachten Sie jedoch, dass diese Kombination von Paketen nicht innerhalb einer einzelnen, diskreten Funktion funktioniert.

Wir empfehlen, jeweils nur eine Funktion zu migrieren und vor dem Fortfahren Tests und Überprüfungen durchzuführen.

Firebase-CLI- und firebase-functions-Versionen prüfen

Achten Sie darauf, dass Sie mindestens die Firebase CLI-Version 12.00 und die 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 aufgebaut. Wir empfehlen, nur das benötigte Modul zu importieren.

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 im SDK der 2. Generation modulare Importe bevorzugt werden, müssen Sie die Triggerdefinitionen so aktualisieren, dass sie die geänderten Importe aus dem vorherigen Schritt widerspiegeln.

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

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 Bodys haben, die stark auf Kontext der 1. Generation oder anbieterspezifische Parameter wie message oder snapshot angewiesen sind, können Sie die in das SDK der 2. Generation integrierten Hilfsprogramme für die Kompatibilität mit der 1. Generation verwenden.

Das SDK der 2. Generation patcht das Ereignisobjekt automatisch mit Gettern, die den Signaturen der 1. Generation entsprechen. So können Sie JavaScript-Destrukturierung verwenden, um diese Eigenschaften direkt in der Handler-Signatur zu extrahieren. Das minimiert die Notwendigkeit, Ihre Funktionslogik neu zu schreiben.

Referenz zur Anbieterzuordnung

Anbieter Argumente der 1. Generation Patched Event Destructuring (2. Generation)
Pub/Sub (message, context) ({ message, context }) => { ... }
Cloud Firestore (snapshot, context) ({ snapshot, context }) => { ... }
Cloud Storage (object, context) ({ object, context }) => { ... }
Realtime Database (snapshot, context) ({ snapshot, context }) => { ... }
Remote Config (version, context) ({ version, context }) => { ... }
Cloud Scheduler (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 zum deklarativen Definieren von Konfigurationsparametern in Ihrem Code unterstützt. Mit dem neuen params-Modul wird die Bereitstellung durch die CLI blockiert, sofern nicht alle Parameter einen gültigen Wert haben. So wird verhindert, dass eine Funktion ohne 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 wurde verworfen 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 und wird daher dringend empfohlen.

  1. Exportkonfiguration mit der Firebase-Befehlszeile 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 die 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 von Laufzeitoptionen hat sich zwischen der 1. und der 2. Generation geändert. In der 2. Generation wurde außerdem eine neue Funktion zum Festlegen von Optionen für alle Funktionen hinzugefügt.

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 bei Funktionen führen, die zur 2. Generation migriert wurden, wenn Sie dem Dienstkonto der 1. Generation spezielle Berechtigungen gewährt haben. Wenn Sie keine Dienstkontoberechtigungen geändert haben, können Sie diesen Schritt überspringen.

Die empfohlene Lösung besteht darin, das vorhandene Standarddienstkonto App Engine der 1. Generation explizit Funktionen zuzuweisen, die Sie zur 2. Generation migrieren möchten, um den Standard der 2. Generation zu überschreiben. Dazu müssen Sie dafür sorgen, dass für jede migrierte Funktion der richtige Wert für serviceAccountEmail festgelegt wird:

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 Dienstkontodetails so ändern, dass sie alle erforderlichen Berechtigungen sowohl für das Standarddienstkonto App Engine (1. Generation) als auch für das Standarddienstkonto Compute Engine (2. Generation) enthalten.

Nebenläufigkeit verwenden

Ein wesentlicher Vorteil von Funktionen der 2. Generation ist die Möglichkeit, dass eine einzelne Funktionsinstanz mehrere Anfragen gleichzeitig verarbeiten kann. So kann die Anzahl der Kaltstarts für Endnutzer erheblich reduziert werden. Standardmäßig ist die Nebenläufigkeit 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 Anpassen der Parallelität können Sie die Leistung verbessern und die Kosten für Funktionen senken. Weitere Informationen zur Nebenläufigkeit in Gleichzeitige Anfragen zulassen.

Verwendung globaler Variablen prüfen

Funktionen der 1. Generation, die nicht für die gleichzeitige Ausführung konzipiert wurden, verwenden möglicherweise globale Variablen, die bei jeder Anfrage festgelegt und gelesen werden. Wenn die Nebenläufigkeit aktiviert ist und eine einzelne Instanz beginnt, mehrere Anfragen gleichzeitig zu verarbeiten, 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 dadurch 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

Genau 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 darauf umstellen.

Es ist nicht möglich, eine Funktion der 1. Generation mit demselben Namen auf die 2. Generation zu aktualisieren und firebase deploy auszuführen. Dies führt zu folgendem Fehler:

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 Typ des Triggers ab, den Ihre Funktion verwendet.

Callable-, Aufgabenwarteschlangen- und HTTP-Trigger migrieren

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. Funktion bereitstellen Sowohl die Funktionen der 1. Generation als auch die der 2. Generation werden jetzt ausgeführt.
  3. Aktualisieren Sie Ihren Clientcode oder Aufrufer, damit er auf den Namen oder die URL der neuen Funktion der 2. Generation verweist.
  4. Sobald der gesamte Traffic auf die neue Funktion umgestellt wurde, löschen Sie die Funktion der 1. Generation mit dem Befehl firebase functions:delete der Firebase-Befehlszeile.

Hintergrund-Trigger migrieren

Hintergrundtrigger (z. B. Pub/Sub-, Cloud Firestore- und Cloud Storage-Trigger) reagieren auf Ereignisse in Ihrem Projekt. Damit während der Umstellung keine Ereignisse verloren gehen, müssen Sie die Funktionen der 1. und 2. Generation vorübergehend parallel ausführen.

Während des Übergangszeitraums 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.

  1. Fügen Sie die Funktion der 2. Generation neben der Funktion der 1. Generation hinzu. Die vorhandene Funktion der 1. Generation bleibt in Ihrem Code erhalten und die Funktion der 2. Generation wird hinzugefügt, die auf dieselbe Ereignisquelle wartet.

    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);
    });
    
  2. Führen Sie firebase deploy aus. Beide Funktionen sind jetzt aktiv und reagieren auf dieselben Ereignisse.

  3. Prüfen Sie, ob die Funktion der 2. Generation Traffic empfängt. Behalten Sie die Logs für beide Funktionen im Blick. Prüfen Sie, ob die Funktion der 2. Generation für alle Ereignisse aufgerufen wird und ob die Aufrufe erfolgreich sind.

  4. Wenn Sie sicher sind, dass die Funktion korrekt ausgeführt wird, verschieben Sie die tatsächliche Geschäftslogik aus der Funktion der 1. Generation in den Hauptteil 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.

  5. Entfernen Sie die Funktionsdefinition der 1. Generation aus Ihrem Code und stellen Sie die Funktion noch einmal bereit. In der Befehlszeile werden Sie aufgefordert, die Funktion der 1. Generation aus Google Cloud zu löschen.