Passer des fonctions Node.js de 1re génération à celles de 2e génération

Nous recommandons aux applications qui utilisent actuellement des fonctions de 1re génération de migrer vers celles de 2e génération en suivant les instructions de ce guide. Les fonctions de 2e génération utilisent Cloud Run pour offrir de meilleures performances, une meilleure configuration, une meilleure surveillance, etc.

Les exemples de cette page supposent que vous utilisez JavaScript avec des modules CommonJS (importations de style require), mais les mêmes principes s'appliquent à JavaScript avec ESM (importations de style import … from) et TypeScript.

Le processus de migration

Les fonctions de 1re et 2e génération peuvent coexister côte à côte dans le même fichier. Cela facilite la migration, élément par élément, dès que vous êtes prêt. Nous vous recommandons de migrer une fonction à la fois, d'effectuer les tests et la vérification avant de continuer.

Vérifier les versions de la CLI Firebase et de firebase-function

Assurez-vous d'utiliser au moins la version 12.00 de la CLI Firebase et la version 4.3.0 de l'firebase-functions. Toutes les versions plus récentes seront compatibles avec les 1re et 2e générations.

Mettre à jour les importations

Les fonctions de 2e génération sont importées à partir du sous-package v2 du SDK firebase-functions. Ce chemin d'importation différent suffit à la CLI Firebase pour déterminer si elle doit déployer le code de votre fonction en tant que fonction de 1re ou 2e génération.

Le sous-package v2 est modulaire. Par conséquent, nous vous recommandons de n'importer que le module spécifique dont vous avez besoin.

Avant : 1re génération

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

Après : 2e génération

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

Mettre à jour les définitions des déclencheurs

Étant donné que le SDK de deuxième génération favorise les importations modulaires, mettez à jour les définitions de déclencheur pour refléter les importations modifiées de l'étape précédente.

Les arguments transmis aux rappels pour certains déclencheurs ont changé. Dans cet exemple, notez que les arguments du rappel onDocumentCreated ont été regroupés dans un seul objet event. De plus, certains déclencheurs disposent de nouvelles fonctionnalités de configuration pratiques, comme l'option cors du déclencheur onRequest.

Avant: 1re génération

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

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

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

Après: 2e génération

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) => {
  /* ... */
});

Utiliser une configuration paramétrée

Les fonctions de 2e génération ne prennent plus en charge functions.config, mais une interface plus sécurisée pour définir des paramètres de configuration de manière déclarative dans votre codebase. Avec le nouveau module params, la CLI bloque le déploiement, sauf si tous les paramètres ont une valeur valide. Cela garantit qu'une fonction n'est pas déployée avec une configuration manquante.

Migrer vers le sous-package params

Si vous avez utilisé la configuration de l'environnement avec functions.config, vous pouvez migrer votre configuration existante vers une configuration paramétrée.

Avant : 1re génération

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

exports.date = functions.https.onRequest((req, res) => {
  const date = new Date();
  const formattedDate =
date.toLocaleDateString(functions.config().dateformat);

  // ...
});

Après: 2e génération

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

const dateFormat = defineString("DATE_FORMAT");

exports.date = onRequest((req, res) => {
  const date = new Date();
  const formattedDate = date.toLocaleDateString(dateFormat.value());

  // ...
});

Définir les valeurs des paramètres

Lors du premier déploiement, la CLI Firebase vous invite à saisir toutes les valeurs des paramètres et les enregistre dans un fichier dotenv. Pour exporter les valeurs fonctions.config, exécutez firebase functions:config:export.

Pour plus de sécurité, vous pouvez également spécifier des types de paramètres et des règles de validation.

Cas particulier : clés API

Le module params s'intègre à Cloud Secret Manager, qui fournit un contrôle précis des accès aux valeurs sensibles telles que les clés API. Pour en savoir plus, consultez la section Paramètres de secret.

Avant : 1re génération

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

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

Après: 2e génération

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

Définir les options d'exécution

La configuration des options d'exécution a changé entre la 1re génération et la 2e génération. La 2e génération ajoute également une nouvelle fonctionnalité permettant de définir des options pour toutes les fonctions.

Avant : 1re génération

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

Après : 2e génération

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) => {
  /* ... */
});

Utiliser la simultanéité

Un avantage important des fonctions de 2e génération est la possibilité pour une seule instance de fonction de répondre à plusieurs requêtes à la fois. Cela peut réduire considérablement le nombre de démarrages à froid rencontrés par les utilisateurs finaux. Par défaut, la simultanéité est définie sur 80, mais vous pouvez la définir sur n'importe quelle valeur comprise entre 1 et 1 000:

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

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

Le paramétrage de la concurrence peut améliorer les performances et réduire les coûts des fonctions. Pour en savoir plus sur la simultanéité, consultez Autoriser les requêtes simultanées.

Auditer l'utilisation des variables globales

Les fonctions de 1re génération écrites sans simultanéité peuvent utiliser des variables globales définies et lues dans chaque requête. Lorsque la simultanéité est activée et qu'une seule instance commence à traiter plusieurs requêtes à la fois, cela peut entraîner des bugs dans votre fonction, car les requêtes simultanées commencent à définir et à lire des variables globales simultanément.

Lors de la mise à niveau, vous pouvez définir le processeur de votre fonction sur gcf_gen1 et définir concurrency sur 1 pour restaurer le comportement de 1re génération:

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

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

Toutefois, cette solution n'est pas recommandée à long terme, car elle perd les avantages de performances des fonctions de 2e génération. Auditez plutôt l'utilisation des variables globales dans vos fonctions, puis supprimez ces paramètres temporaires lorsque vous êtes prêt.

Migrer le trafic vers les nouvelles fonctions de 2e génération

Tout comme lorsque vous modifiez la région ou le type de déclencheur d'une fonction, vous devrez attribuer un nouveau nom à la fonction de 2e génération et migrer progressivement le trafic vers celle-ci.

Il n'est pas possible de passer d'une fonction de 1re génération à une fonction de 2e génération avec le même nom et d'exécuter firebase deploy. L'erreur suivante se produira :

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

Avant de suivre ces étapes, assurez-vous d'abord que votre fonction est idempotente, car la nouvelle et l'ancienne version s'exécuteront en même temps lors de la modification. Par exemple, si vous disposez d'une fonction de 1re génération qui répond aux événements d'écriture dans Firestore, assurez-vous que la réponse à une écriture deux fois, une fois par la fonction de 1re génération et une fois par la fonction de 2e génération, en réponse à ces événements laisse votre application dans un état cohérent.

  1. Renommez la fonction dans le code de vos fonctions. Par exemple, renommez resizeImage en resizeImageSecondGen.
  2. Déployez la fonction afin que la fonction d'origine de 1re génération et la fonction de 2e génération soient exécutées.
    1. Dans le cas des déclencheurs appelables, de la file d'attente de tâches et des déclencheurs HTTP, commencez à rediriger tous les clients vers la fonction de 2e génération en mettant à jour le code client avec le nom ou l'URL de la fonction de 2e génération.
    2. Avec les déclencheurs en arrière-plan, les fonctions de 1re et de 2e génération répondent à chaque événement immédiatement après le déploiement.
  3. Une fois que tout le trafic a été migré, supprimez la fonction de 1re génération à l'aide de la commande firebase functions:delete de la CLI Firebase.
    1. Vous pouvez également renommer la fonction de 2e génération pour qu'elle corresponde au nom de la fonction de 1re génération.