Fazer upgrade das funções do Node.js de 1ª para a 2ª geração

Os apps que atualmente usam funções de 1ª geração precisam migrar para a 2ª geração usando as instruções deste guia. As funções de 2ª geração usam o Cloud Run para melhorar o desempenho, a configuração, o monitoramento e muito mais.

Os exemplos nesta página presumem que você esteja usando JavaScript com módulos CommonJS (importações de estilo require), mas os mesmos princípios se aplicam a JavaScript com ESM (importações de estilo import … from) e TypeScript.

Processo de migração

As funções da 1ª geração e da 2ª geração podem coexistir lado a lado no mesmo arquivo. Isso facilita a migração por partes, quando você estiver tudo pronto. Recomendamos migrar uma função de cada vez, realizando testes e verificações antes de prosseguir.

Verificar a CLI do Firebase e as versões de firebase-function

Verifique se você está usando pelo menos a versão 12.00 da CLI do Firebase e a versão 4.3.0 do firebase-functions. Qualquer versão mais recente será compatível com a 2ª e a 1ª geração.

Atualizar importações

As funções de 2ª geração são importadas do subpacote v2 no SDK do firebase-functions. Esse caminho de importação diferente é tudo o que a CLI do Firebase precisa para determinar se o código de função será implantado como uma função de 1ª ou 2ª geração.

O subpacote v2 é modular. Recomendamos importar apenas o módulo necessário.

Antes: 1ª geração

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

Depois: 2ª geração

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

Atualizar definições de acionadores

Como o SDK da 2ª geração favorece importações modulares, atualize as definições de gatilho para refletir as importações alteradas da etapa anterior.

Os argumentos passados para os callbacks de alguns acionadores foram alterados. Neste exemplo, os argumentos do callback onDocumentCreated foram consolidados em um único objeto event. Além disso, alguns gatilhos têm novos recursos de configuração convenientes, como a opção cors do gatilho onRequest.

Antes: 1ª geração

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

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

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

Depois: 2ª geração

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

Usar a configuração parametrizada

As funções de 2ª geração são compatíveis com functions.config em favor de uma interface mais segura para definir parâmetros de configuração de maneira declarativa dentro da sua base de código. Com o novo módulo params, a CLI bloqueia a implantação, a menos que todos os parâmetros tenham um valor válido, garantindo que uma função não seja implantada com a configuração ausente.

Migrar para o subpacote params

Se você estiver usando a configuração do ambiente com functions.config, será possível migrar a configuração existente para a configuração parametrizada.

Antes: 1ª geração

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

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

  // ...
});

Depois: 2ª geração

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

  // ...
});

Definir valores de parâmetros

Na primeira implantação, a CLI do Firebase solicita todos os valores de parâmetros e os salva em um arquivo dotenv. Para exportar os valores de functions.config, execute firebase functions:config:export.

Para mais segurança, também é possível especificar tipos de parâmetros e regras de validação.

Caso especial: chaves de API

O módulo params integra-se ao Cloud Secret Manager, que fornece controle de acesso refinado para valores confidenciais, como chaves de API. Consulte os parâmetros secretos para mais informações.

Antes: 1ª geração

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

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

Depois: 2ª geração

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

Definir opções do ambiente de execução

A configuração de opções de tempo de execução foi alterada entre a 1ª e a 2ª geração. A 2ª geração também adiciona um novo recurso para definir opções para todas as funções.

Antes: 1ª geração

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

Depois: 2ª geração

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

Usar simultaneidade

Uma vantagem significativa das funções de 2ª geração é a capacidade de uma única instância de função atender a mais de uma solicitação de uma só vez. Isso pode reduzir drasticamente o número de inicializações a frio testadas pelos usuários finais. Por padrão, a simultaneidade é definida como 80, mas é possível defini-la com qualquer valor de 1 a 1.000:

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

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

O ajuste da simultaneidade pode melhorar o desempenho e reduzir o custo das funções. Saiba mais sobre a simultaneidade em Permitir solicitações simultâneas.

Auditar o uso de variáveis globais

As funções de 1ª geração escritas sem simultaneidade em mente podem usar variáveis globais definidas e lidas em cada solicitação. Quando a simultaneidade está ativada e uma única instância começa a processar várias solicitações de uma só vez, isso pode introduzir bugs na sua função, já que as solicitações simultâneas começam a configurar e ler as variáveis globais simultaneamente.

Ao fazer upgrade, você pode definir a CPU da função como gcf_gen1 e definir concurrency como 1 para restaurar o comportamento da 1ª geração:

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

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

No entanto, isso não é recomendado como uma correção de longo prazo, já que perde as vantagens de desempenho das funções de 2ª geração. Em vez disso, faça uma auditoria do uso de variáveis globais nas funções e remova essas configurações temporárias quando estiver tudo pronto.

Migrar o tráfego para as novas funções de 2ª geração

Assim como ocorre na alteração da região ou o tipo de gatilho de uma função, será necessário dar um novo nome à função de 2ª geração e migrar o tráfego lentamente para ela.

Não é possível fazer upgrade de uma função da 1ª para a 2ª geração com o mesmo nome e executar firebase deploy. Isso vai resultar no erro:

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

Antes de prosseguir, verifique se a função é idempotente, já que a versão antiga e a nova serão executadas simultaneamente durante a modificação. Por exemplo, se você tiver uma função de 1ª geração que responda a eventos de gravação no Firestore, é necessário responder a uma gravação duas vezes: uma pela 1ª geração e outra pela 2ª. Isso faz com que o app fique em um estado consistente.

  1. Renomeie a função no código de funções. Por exemplo, renomeie resizeImage como resizeImageSecondGen.
  2. Implante a função para que as funções originais de 1ª e de 2ª geração sejam executadas.
    1. No caso de acionadores HTTP, chamáveis e de fila de tarefas, comece a direcionar todos os clientes para a função de 2ª geração. Para isso, atualize o código do cliente com o nome ou o URL dessa função.
    2. Com os gatilhos em segundo plano, as funções de 1ª e 2ª geração vão responder a todos os eventos imediatamente após a implantação.
  3. Quando a migração de todo o tráfego for concluída, exclua a função de 1ª geração usando o comando firebase functions:delete da CLI do Firebase.
    1. Se quiser, renomeie a função de 2ª geração para corresponder ao nome da função de 1ª geração.