Actualiza las funciones de Node.js de 1ª gen. a 2ª gen.

Si tienes apps que usan funciones de 1ª gen., considera migrar a las de 2ª gen. mediante las instrucciones de esta guía. Las funciones de 2ª gen. usan Cloud Run para proporcionar mejor rendimiento, configuración, supervisión y mucho más.

En los ejemplos de esta página, suponemos que usas JavaScript con módulos de CommonJS (importaciones de estilo require), sin embargo, los mismos principios se aplican a JavaScript con ESM (importaciones de estilo import … from) y TypeScript.

El proceso de migración

Las funciones de 1ª y 2ª gen. pueden coexistir en paralelo en el mismo archivo. Esto permite una migración sencilla paso a paso, a medida que esté todo preparado. Te recomendamos que migres una función a la vez y que realices pruebas y verificaciones antes de continuar.

Verifica las versiones de Firebase CLI y firebase-function

Asegúrate de usar al menos la versión 12.00 de Firebase CLI y la versión 4.3.0 de firebase-functions. Las versiones más recientes serán compatibles con la 2ª gen. y la 1ª gen.

Actualiza las importaciones

Las funciones de 2ª gen. importan desde el subpaquete v2 en el SDK de firebase-functions. Esta ruta de importación diferente es todo lo que Firebase CLI necesita para determinar si debe implementar el código de tu función como una función de 1ª o 2ª gen.

El subpaquete v2 es modular, y te recomendamos que solo importes el módulo específico que necesitas.

Antes: 1ª gen.

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

Después: 2ª gen.

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

Actualiza las definiciones de activadores

Dado que el SDK de la 2ª gen. prioriza las importaciones modulares, actualiza las definiciones del activador para reflejar las importaciones modificadas del paso anterior.

Cambiaron los argumentos que se pasaron a las devoluciones de llamada para algunos activadores. En este ejemplo, ten en cuenta que los argumentos para la devolución de llamada de onDocumentCreated se consolidaron en un solo objeto event. Además, algunos activadores tienen funciones de configuración nuevas y convenientes, como la opción cors del activador onRequest.

Antes: 1ª gen.

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

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

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

Después: 2ª gen.

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

Utiliza la configuración parametrizada

Las funciones de la 2ª gen. pierden compatibilidad con functions.config para favorecer una interfaz más segura para definir parámetros de configuración de forma declarativa dentro de tu base de código. Con el nuevo módulo params, la CLI bloquea la implementación, a menos que todos los parámetros tengan un valor válido, lo que garantiza que una función no se implemente con configuración faltante.

Migra al subpaquete params

Si usas la configuración del entorno con functions.config, puedes migrar tu configuración existente a la configuración parametrizada.

Antes: 1ª gen.

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

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

  // ...
});

Después: 2ª gen.

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

  // ...
});

Configura los valores de parámetros

La primera vez que realices una implementación, Firebase CLI solicitará todos los valores de los parámetros y los guardará en un archivo dotenv. Para exportar los valores de functions.config, ejecuta firebase functions:config:export.

Para mayor seguridad, también puedes especificar los tipos de parámetros y las reglas de validación.

Caso especial: Claves de API

El módulo params se integra en Cloud Secret Manager, que proporciona un control de acceso detallado a los valores sensibles, como las claves de API. Consulta los parámetros secretos para obtener más información.

Antes: 1ª gen.

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

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

Después: 2ª gen.

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

Configura las opciones del entorno de ejecución

La configuración de las opciones del entorno de ejecución cambió entre la 1ª y la 2ª gen. En el caso de la 2ª gen., también se agrega una nueva función que permite configurar opciones para todas las funciones.

Antes: 1ª gen.

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

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

Después: 2ª gen.

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

Utiliza la simultaneidad

Una ventaja significativa de las funciones de 2ª gen. es la capacidad de que una única instancia de función pueda entregar más de una solicitud a la vez. Esto puede reducir drásticamente la cantidad de inicios en frío que experimentan los usuarios finales. De forma predeterminada, la simultaneidad se define en 80, pero puedes establecerla en cualquier valor entre 1 y 1,000:

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

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

El ajuste de la simultaneidad puede mejorar el rendimiento y reducir el costo de las funciones. Obtén más información sobre la simultaneidad en Permitir solicitudes simultáneas.

Audita el uso de las variables globales

Las funciones de 1ª gen. escritas sin considerar la simultaneidad pueden usar variables globales que se configuran y leen en cada solicitud. Cuando la simultaneidad está habilitada y una sola instancia comienza a controlar varias solicitudes a la vez, es posible que se generen errores en la función, ya que las solicitudes simultáneas comenzarán a configurar y leer las variables globales de forma simultánea.

Durante la actualización, puedes establecer la CPU de tu función en gcf_gen1 y concurrency en 1 para restablecer el comportamiento de 1ª gen.:

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

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

Sin embargo, no se recomienda como solución a largo plazo, ya que perderá las ventajas de rendimiento de las funciones de 2ª gen. En su lugar, audita el uso de variables globales en tus funciones y quita esta configuración temporal cuando tengas todo listo.

Migra el tráfico a las nuevas funciones de 2ª gen.

Al igual que cuando cambias la región o el tipo de activador de una función, deberás asignarle un nombre nuevo a la función de 2ª gen. y migrar lentamente el tráfico hacia ella.

No es posible actualizar una función de 1ª a 2ª gen. con el mismo nombre y ejecutar firebase deploy. Si lo haces, se mostrará el siguiente error:

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

Antes de hacerlo, asegúrate de que tu función sea idempotente, ya que se ejecutarán al mismo tiempo la versión nueva y la antigua de la función durante el cambio. Por ejemplo, si tienes una función de 1ª gen. que responde a eventos de escritura en Firestore, asegúrate de responder a una escritura dos veces, una vez mediante la función de 1ª gen. y una vez mediante la de 2ª gen. En respuesta a esos eventos, tu aplicación tendrá un estado coherente.

  1. Cambia el nombre de la función en el código de tus funciones. Por ejemplo, cambia el nombre de resizeImage a resizeImageSecondGen.
  2. Implementa la función para que se ejecuten tanto la función original de 1ª como la de 2ª gen.
    1. En el caso de los activadores de listas de tareas en cola, de HTTP y los que admiten llamadas, comienza a dirigir a todos los clientes a la función de 2ª gen. mediante la actualización del código del cliente con el nombre o la URL de la función de 2ª gen.
    2. Con los activadores en segundo plano, las funciones de 1ª y 2ª gen. responderán a todos los eventos inmediatamente después de la implementación.
  3. Cuando se migre todo el tráfico, borra la función de 1ª gen. con el comando firebase functions:delete de Firebase CLI.
    1. De manera opcional, cambia el nombre de la función de 2ª gen. para que coincida con el nombre de la de 1ª gen.