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, Konfiguration und Überwachung zu bieten.

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

Der Migrationsvorgang

Funktionen der 1. Generation und der 2. Generation können in derselben Quelldatei nebeneinander vorhanden sein. So können Sie Ihre Codebasis nach und nach migrieren. Beachten Sie jedoch, dass diese Kombination von Paketen nicht innerhalb einer einzelnen, separaten Funktion funktioniert.

Wir empfehlen, jeweils eine Funktion zu migrieren und Tests und Überprüfungen durchzuführen, bevor Sie fortfahren.

Firebase CLI- und firebase-functions-Versionen 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 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 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 vom 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.

Anbieterzuordnung

Anbieter Argumente der 1. Generation Gepatchte Ereignisdestrukturierung der 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 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 können Sie auch Optionen für alle Funktionen festlegen.

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.App Engine Dazu müssen Sie dafür sorgen, 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 die Optimierung der Nebenläufigkeit 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.

Während des Upgrades 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 ist jedoch keine empfohlene langfristige Lösung, 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.

Aufrufbare, 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. 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 Firebase CLI's firebase functions:delete command.

Hintergrundtrigger migrieren

Hintergrundtrigger (z. B. Pub/Sub, Cloud Firestore, und Cloud Storage Trigger) reagieren auf Ereignisse in Ihrem Projekt. Um während der Migration keine Ereignisse zu verpassen, müssen Sie sowohl die Funktion der 1. Generation als auch die 2. Generation vorübergehend parallel 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.

  1. Fügen Sie die Funktion der 2. Generation neben der Funktion der 1. Generation hinzu. 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.

    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 warten auf dieselben Ereignisse.

  3. Prüfen Sie, ob die Funktion der 2. Generation Traffic empfängt. Überwachen 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.

  4. Wenn Sie sicher sind, dass die Funktion korrekt funktioniert, verschieben Sie die eigentliche Geschäftslogik aus 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.

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