Neste guia, mostramos como migrar suas extensões do ambiente Firebase Extensions descontinuado para uma função que seus usuários instalam e implantam no próprio Cloud Functions para a base de código Firebase (2ª geração).
Esse é o caminho de migração recomendado. O Firebase vai manter uma lista de extensões com equivalentes oficiais do npm. Este guia mostra como criar as suas.
Neste guia, a extensão Transmitir Cloud Firestore para
BigQuery (firestore-bigquery-export) é usada como exemplo. Cada seção termina com um Exemplo prático que mostra como era a extensão antes e depois da migração, como o pacote @firebase-function-kits/firestore-bigquery-export.
Inscreva-se para receber mais informações e ajuda na migração de extensões
Se você tiver dúvidas sobre como migrar do Firebase Extensions, entre em contato pelo e-mail firebase-extensions-migrator-support-external@google.com. Também vamos enviar um e-mail para esse grupo quando atualizarmos o guia com mais informações sobre como empacotar, testar e distribuir suas funções de 2ª geração.
Para participar desse grupo, envie uma mensagem para firebase-extensions-migrator-support-external+subscribe@google.com, que vai responder com um e-mail de solicitação de participação. Você precisa responder a esse e-mail, não clicar no botão "Participar deste grupo".
Antes de começar
Para concluir essa migração conforme descrito, você vai usar os seguintes recursos do Cloud Functions:
Configuração parametrizada. Cada parâmetro declarado em
extension.yamlse torna um parâmetro definido no código do pacote.Papéis declarativos do IAM e APIs necessárias. Cada função declarada em
extension.yamlse torna uma chamadarequiresRole(...), e cada API se torna uma chamadarequiresAPI(...)no código do pacote. No momento da implantação, a CLI Firebase concede os papéis declarados a uma conta de serviço de ambiente de execução gerenciado e ativa as APIs declaradas em seu nome.Eventos de ciclo de vida para bases de código Cloud Functions. As bases de código do Cloud Functions agora são compatíveis com eventos de ciclo de vida análogos ao Firebase Extensions. Declare a configuração de instalação e atualização com os hooks de ciclo de vida
afterFirstDeploy(...)eafterRedeploy(...). Eles substituem olifecycleEventsdeclarado emextension.yaml.
Migrar a origem Firebase Extensions para uma função de 2ª geração
(Opcional) Migração automatizada com a habilidade do agente Firebase
É possível automatizar as etapas de 1 a 8 (inventário de recursos, acionamento de
upgrades, conversões de parâmetros e secrets, IAM declarativo, hooks de ciclo de vida e
geração do README do pacote) usando a habilidade oficial do agente de IA
extension-to-functions-codebase.
Instalar a habilidade
Se você ou seu assistente de programação de IA (Gemini em Firebase, Cursor, Claude Code, GitHub Copilot) ainda não instalou a habilidade, execute o comando a seguir usando a CLI de habilidades:
npx skills add firebase/agent-skills --skill extension-to-functions-codebase
Depois que a habilidade for instalada no seu projeto, o assistente de programação de IA vai seguir automaticamente as regras de migração e as etapas de transformação. Você pode usar o seguinte comando:
"Migre esta extensão do Firebase para um pacote publicável do Function Kit de 2ª geração
seguindo as instruções na habilidade extension-to-functions-codebase."
1. Inventariar a extensão
Comece fazendo um inventário da extensão: uma lista completa de tudo que ela declara, envia e documenta. Assim, cada comportamento tem um destino definido na função de 2ª geração, e nada é perdido na migração.
Revise cada um dos itens a seguir e anote o que encontrar:
extension.yaml, que declara seus parâmetros, funções, eventos, papéis do IAM, APIs obrigatórias, secrets e hooks de ciclo de vida.functions/, que contém o código da função, dependências, configuração de build, acionadores e funções de fila de tarefas.README.md,PREINSTALL.mdePOSTINSTALL.md, que contêm etapas de configuração, avisos e observações de faturamento.scripts/, que contém utilitários de importação, backfill, IAM, reparo ou migração e outras ferramentas enviadas com a extensão.
Em seguida, para cada item em
extension.yaml, decida onde ele
fica no pacote npm:
Converta a configuração do usuário em parâmetros Cloud Functions (Etapa 4).
Converta os secrets em secrets do Cloud Functions (Etapa 4).
Converter papéis do IAM em declarações
requiresRole(...)(Etapa 6).Converta as APIs do Google necessárias em declarações
requiresAPI(...)quando apropriado (Etapa 6).Converta os hooks de instalação e atualização em declarações
afterFirstDeploy(...)eafterRedeploy(...)(Etapa 7).Converta os IDs de instância de
EXT_INSTANCE_IDparaFIREBASE_KIT_INSTANCE_ID(etapa 4).
Exemplo prático:transmissão de Cloud Firestore para BigQuery
A leitura de firestore-bigquery-export/extension.yaml e functions/ gera este inventário:
Em extension.yaml |
Contagem / valor | Para onde ele vai |
|---|---|---|
params |
25 (COLLECTION_PATH, DATASET_ID, TABLE_ID, DATASET_LOCATION, VIEW_TYPE, …) |
Cloud Functions params (Etapa 4) |
apis |
bigquery.googleapis.com |
requiresAPI(...) (Etapa 6) |
roles |
bigquery.dataEditor, datastore.user, bigquery.user |
requiresRole(...) (Etapa 6) |
resources |
1 gatilho de evento (fsexportbigquery) + funções de fila de tarefas (initBigQuerySync, setupBigQuerySync) |
Funções de pacote exportadas (etapa 3) |
lifecycleEvents |
onInstall → initBigQuerySync; onUpdate / onConfigure → setupBigQuerySync |
afterFirstDeploy / afterRedeploy (Etapa 7) |
| ID da instância | Não usado (sem leituras de EXT_INSTANCE_ID) |
Nada para migrar |
scripts/ |
import/ (preenchimento), gen-schema-view/ |
Mantidos como scripts (fora do escopo aqui) |
Análise. A extensão não declara parâmetros type: secret. Portanto, não há nada para migrar para secrets na Etapa 4. O acionador de eventos já é de 2ª geração. Somente as funções de fila de tarefas ainda são de 1ª geração (relevante na Etapa 3).
2. Atualizar package.json
Atualize o arquivo package.json da extensão. Se você estiver migrando uma extensão,
esse pode ser o package.json raiz. Se você estiver migrando muitas extensões em um
repositório, dê a cada uma delas um pacote próprio.
Versões mínimas do SDK:declare
firebase-functions >=
7.4.0 e firebase-admin >=
14.2.0 como dependências. Declare sua versão de firebase-functions como uma dependência
de mesmo nível para que o projeto Cloud Functions dos usuários tenha a mesma
versão do SDK com que sua biblioteca foi escrita.
{
"name": "<package-name>",
"version": "1.0.0",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"exports": {
".": {
"types": "./lib/index.d.ts",
"default": "./lib/index.js"
}
},
"engines": {
"node": "22"
},
"peerDependencies": {
"firebase-functions": "^7.4.0"
},
"dependencies": {
"firebase-functions": "^7.4.0",
"firebase-admin": "^14.2.0"
}
}
Exemplo prático:transmissão de Cloud Firestore para BigQuery
Antes. O functions/package.json da extensão é particular, nomeia o
ID da extensão e declara firebase-functions como uma dependência direta:
{
"name": "firestore-bigquery-export",
"main": "lib/index.js",
"private": true,
"dependencies": {
"@firebaseextensions/firestore-bigquery-change-tracker": "^2.0.4",
"firebase-admin": "^14.2.0",
"firebase-functions": "^6.3.2"
}
}
Depois. Um pacote publicável: nome com escopo, um mapa exports e
firebase-functions movido para peerDependencies:
{
"name": "@firebase-function-kits/firestore-bigquery-export",
"version": "0.1.0",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"exports": {
".": {
"types": "./lib/index.d.ts",
"default": "./lib/index.js"
}
},
"engines": {
"node": "22"
},
"peerDependencies": {
"firebase-functions": "^7.4.0"
},
"dependencies": {
"@firebaseextensions/firestore-bigquery-change-tracker": "^2.0.4",
"firebase-admin": "^14.2.0",
"firebase-functions": "^7.4.0"
}
}
3. Fazer upgrade das funções da 1ª para a 2ª geração
Se a extensão ainda exportar funções de 1ª geração, converta cada acionador para o equivalente de
2ª geração. Importe dos módulos firebase-functions/... e transmita
configurações de tempo de execução nas opções de acionamento.
Consulte o guia de upgrade do Cloud Functions de 2ª geração. É importante ressaltar que você pode minimizar os esforços de reescrita usando a desestruturação de eventos corrigidos de 2ª geração e evitar reescrever a lógica da função, porque o SDK de 2ª geração expõe os parâmetros da v1 como campos no objeto de evento. Isso permite usar parâmetros desestruturados/nomeados e manter a lógica de negócios inalterada.
Antes. 1ª geração:
import * as functions from "firebase-functions/v1";
export const sync = functions.firestore
.document("{collectionId}/{documentId}")
.onWrite(async (change, context) => {
await handleWrite(change.before, change.after, context.params);
});
Depois. 2ª geração:
import { onDocumentWritten } from "firebase-functions/firestore";
export const syncV2 = onDocumentWritten(
{ document: "{collectionId}/{documentId}" },
async ({ change, context }) =>
await handleWrite(change.before, change.after, context.params)
);
Consulte a comparação de versões do Cloud Functions para uma lista completa das diferenças entre as funções de 1ª e 2ª geração.
Uma diferença significativa entre as funções de 1ª e 2ª geração é que as funções de 2ª geração precisam estar localizadas no mesmo lugar que os recursos de acionamento. Quando os usuários migram de uma extensão que usa funções de 1ª geração para um kit que usa funções de 2ª geração, eles podem precisar mudar os locais das funções para atender a esse requisito.
Qualquer local do Cloud Functions selecionado pelos usuários pode ser interrompido durante a migração porque o local atual é preservado. Se as funções acionadas por eventos da sua extensão dependiam do local das funções fornecidas pelo usuário, sugerimos adicionar um novo parâmetro à extensão para coletar o local do acionador de eventos e mapear isso para o novo local da função.
Exemplo prático:transmissão de Cloud Firestore para BigQuery
defineString("DATABASE_REGION", {
label: "Firestore Instance Location",
description:
"Where is the Firestore database located? You can check your current database location at https://console.cloud.google.com/firestore/databases. The functions in this kit deploy to the Cloud Run region closest to this location.",
input: select({
"Multi-region (Europe - Belgium and Netherlands)": "eur3",
"Multi-region (United States)": "nam5",
"Multi-region (Iowa, North Virginia, and Oklahoma)": "nam7",
"Iowa (us-central1)": "us-central1",
// More locations...
})
});
// Firestore multi-region locations are not Cloud Run regions; deploying a
// function to one hard-fails, so they map to a region inside the multi-region.
const MULTI_REGION_TO_FUNCTION_REGION: Record<string, string> = {
nam5: "us-central1",
nam7: "us-central1",
eur3: "europe-west1",
};
/**
* Maps a Firestore database location to the Cloud Run region the functions
* should deploy to. The lookup is case-insensitive and ignores surrounding
* whitespace, as the CLI's own region handling is. Regional locations pass
* through lowercased; an unset or blank location returns `undefined`, meaning
* the functions declare no region.
*/
export function firestoreLocationToFunctionRegion(
location: string | undefined
): string | undefined {
const normalized = location?.trim().toLowerCase();
if (!normalized) {
return undefined;
}
return MULTI_REGION_TO_FUNCTION_REGION[normalized] ?? normalized;
}
const functionRegion = firestoreLocationToFunctionRegion(
process.env.DATABASE_REGION
);
export const fsexportbigquery = onDocumentWritten(
{
region: functionRegion,
// Other configuration
},
(event) => handleDocumentWrite(event, getHandlerContext())
);
Na primeira implantação do kit, os usuários vão receber uma solicitação para informar o
DATABASE_REGION, e as funções serão implantadas no
functionRegion correspondente acima, corrigindo problemas de local para as funções
acionadas por eventos de segunda geração.
4. Converter parâmetros e secrets de extensão
Parâmetros
Cada parâmetro declarado em extension.yaml se torna um parâmetro Cloud Functions.
Converter leituras diretas do ambiente:
const collectionPath = process.env.COLLECTION_PATH;
em parâmetros Cloud Functions:
import { defineString } from "firebase-functions/params";
import { onDocumentWritten } from "firebase-functions/firestore";
const collectionPath = defineString("COLLECTION_PATH");
// Pass the param directly when used as a placeholder (e.g. trigger path)
export const sync = onDocumentWritten(
{ document: collectionPath },
async (event) => {
// Call .value() to read the string inside a handler
const path = collectionPath.value();
await handleWrite(path, event);
}
);
Use collectionPath.value() para ler a string dentro de um gerenciador; use collectionPath diretamente onde um marcador de posição é esperado, como um caminho de acionador de função.
A CLI Firebase descobre seus parâmetros e lê os valores deles em .env,
.env.<projectId> ou solicita aos usuários durante a implantação. Mantenha os mesmos nomes de parâmetros para que os valores de uma instalação atual sejam transferidos.
É importante não mudar os nomes de parâmetros declarados no seu código de forma alguma. A migração de extensão preserva automaticamente os valores de parâmetro do usuário final, mas apenas quando os nomes permanecem inalterados.
Exemplo prático:transmissão de Cloud Firestore para BigQuery
Antes. Um parâmetro declarado em extension.yaml, lido como uma variável de ambiente
bruta em config.ts:
# extension.yaml
- param: COLLECTION_PATH
label: Collection path
type: string
required: true
// functions/src/config.ts
collectionPath: process.env.COLLECTION_PATH,
Depois. Um defineString. A CLI o descobre e lê de .env:
// src/config.ts
import { defineString } from "firebase-functions/params";
collectionPath: defineString("COLLECTION_PATH", {
label: "Collection path",
// We now support "nonEmpty: true" to ensure a value other than the empty string
// is entered, analogous to "required: true" in extension.yaml
input: { text: { nonEmpty: true } }
}),
O nome do parâmetro não mudou, então um .env atual continua funcionando.
ID da instância
As extensões leem o ID da instância de EXT_INSTANCE_ID, injetado pelo
ambiente de execução das extensões. Os kits de função leem o ID da instância de
FIREBASE_KIT_INSTANCE_ID, que a CLI Firebase define para cada instância
do kit como a chave da instância no mapa instances em firebase.json. A
CLI fornece isso durante a descoberta no momento da implantação, no emulador e para as
funções implantadas.
O ID da instância não é um parâmetro. Portanto, não o declare com defineString. Na verdade, FIREBASE_... é um prefixo reservado em arquivos .env, então os usuários não podem definir nem substituir esse prefixo. Os valores injetados pela CLI não ficam visíveis para o sistema de parâmetros. Leia diretamente no ambiente:
// Before
const instanceId = process.env.EXT_INSTANCE_ID;
// After
const instanceId = process.env.FIREBASE_KIT_INSTANCE_ID;
A variável só será definida quando o pacote for implantado como um kit. Se o código também for implantado como uma base de código independente (consulte a Etapa 9), trate-o como opcional ou falhe rapidamente com uma mensagem clara quando ele estiver faltando. Se a extensão expôs o ID da instância como um parâmetro voltado ao usuário, remova esse parâmetro, já que a CLI Firebase agora é proprietária do valor.
Exemplo prático:excluir dados do usuário
A extensão Stream Cloud Firestore para BigQuery não lê o ID da instância. Portanto, não há nada para migrar. A extensão "Excluir dados do usuário" usa esse recurso para nomear os tópicos do Pub/Sub.
Antes. Lido como uma variável de ambiente bruta em config.ts com o prefixo ext-
que as extensões usavam para os recursos:
// functions/src/config.ts
discoveryTopic: `ext-${process.env.EXT_INSTANCE_ID}-discovery`,
deletionTopic: `ext-${process.env.EXT_INSTANCE_ID}-deletion`,
Depois. Uma leitura simples de process.env de FIREBASE_KIT_INSTANCE_ID usada para
dois parâmetros comuns para que os usuários possam substituir os nomes dos temas:
// src/config.ts
const instanceId = process.env.FIREBASE_KIT_INSTANCE_ID;
// Non-empty defaults so Pub/Sub trigger bindings resolve during deploy
// discovery without freezing an empty topic name into the manifest.
discoveryTopicName: defineString("DISCOVERY_TOPIC_NAME", {
default: `kit-${instanceId}-discovery`,
}),
deletionTopicName: defineString("DELETION_TOPIC_NAME", {
default: `kit-${instanceId}-deletion`,
}),
Os padrões não podem estar vazios porque as vinculações de gatilho são resolvidas no momento da descoberta. Um padrão vazio seria gravado no manifesto de implantação como o nome do tópico. O kit também se defende contra a execução fora do contexto
de um kit. Se a variável estiver faltando, o padrão no nível do módulo será avaliado como
kit-undefined-discovery. Portanto, o carregador de configuração falha com um erro explicativo
em vez disso:
// ...
const instanceId = process.env.FIREBASE_KIT_INSTANCE_ID;
if (!instanceId) {
throw new Error(
"FIREBASE_KIT_INSTANCE_ID is not set. It is provided automatically to " +
"kit instances by firebase-tools >= 15.32.0; deploy or emulate this " +
"kit with a supported CLI version."
);
}
// ...
Essa verificação é executada quando um gerenciador resolve a configuração pela primeira vez. Assim, uma variável ausente produz um erro de tempo de execução claro em vez de funções vinculadas silenciosamente a tópicos kit-undefined-*. Para rejeitar a implantação, faça a verificação no escopo do módulo para que ela seja executada durante a descoberta. Como a CLI deriva o ID da instância de firebase.json, não há um INSTANCE_ID configurável nem nada para manter sincronizado em várias instâncias.
Secrets
Em extension.yaml, você declara secrets com type: secret. O tempo de execução das extensões
armazena e vincula esses dados para que o código da extensão possa ler
process.env.PARAM_NAME diretamente. Em uma base de código Cloud Functions típica,
você declara e vincula cada segredo explicitamente:
import { defineSecret } from "firebase-functions/params";
import { onRequest } from "firebase-functions/https";
const apiKey = defineSecret("API_KEY");
export const fn = onRequest({ secrets: [apiKey] }, handler);
Depois que a extensão é migrada para um pacote/kit npm, as referências secretas são
gerenciadas no arquivo .env do usuário final. É importante não mudar os nomes secretos declarados no código de forma alguma. Durante a migração, os segredos do usuário final são migrados de acordo.
Exemplo prático:acionar e-mail de Cloud Firestore
Antes. MAIL_COLLECTION e SMTP_PASSWORD são lidas como variáveis de ambiente
brutas em config.ts:
# extension.yaml
- param: MAIL_COLLECTION
label: Email documents collection
type: string
default: mail
required: true
- param: SMTP_PASSWORD
label: SMTP password
type: secret
// functions/src/config.ts
mailCollection: process.env.MAIL_COLLECTION,
smtpPassword: process.env.SMTP_PASSWORD,
Depois. Um defineString e um defineSecret. A CLI descobre os dois
e lê de .env:
import { defineString, defineSecret } from "firebase-functions/params";
import { onDocumentWritten } from "firebase-functions/firestore";
const mailCollection = defineString("MAIL_COLLECTION", {
label: "Email documents collection",
default: "mail"
});
const smtpPassword = defineSecret("SMTP_PASSWORD", { label: "SMTP password" });
export const processQueue = onDocumentWritten(
{ document: `${mailCollection}/{documentId}`, secrets: [smtpPassword] },
async (event) => {
const collection = mailCollection.value();
const password = smtpPassword.value();
// ...
}
);
5. Migrar chamadas internas da fila de tarefas
Algumas extensões enfileiram o trabalho nas próprias filas de tarefas de dentro do código da função, usando Firebase Admin SDK. Isso é diferente de receber uma tarefa
despachada (abordada nas seções Fazer upgrade de funções e Converter hooks
de ciclo de vida). Aqui, seu código é o produtor que chama
queue.enqueue(...).
As versões anteriores do Admin SDK exigiam que as extensões transmitissem o próprio
ID da instância da extensão como um segundo parâmetro para segmentar uma função da fila de tarefas na
mesma extensão. A partir da versão 14.2.0 do firebase-admin, isso não é necessário nem recomendado. Por padrão, a API Task Queue agora tem como destino filas de tarefas no mesmo contexto (por exemplo, uma extensão ou um kit). É seguro e recomendável remover
esse parâmetro do código, tanto como uma extensão quanto como funções independentes.
A remoção desse parâmetro garante portabilidade e compatibilidade futura.
Todo o restante da chamada enqueue — o
caminho do recurso locations/<region>/functions/<name>, o payload da tarefa e a lógica
de nova tentativa — permanece igual.
Consulte Enfileirar funções com o Cloud Tasks para mais detalhes sobre como enfileirar funções com Cloud Tasks.
Antes. Extensão da 1ª geração:
import { getFunctions } from "firebase-admin/functions";
const queue = getFunctions().taskQueue(
`locations/${config.location}/functions/syncBigQuery`,
process.env.EXT_INSTANCE_ID, // extension instance ID, injected by the runtime
);
await queue.enqueue(taskData);
Depois. Extensão de 2ª geração:
import { getFunctions } from "firebase-admin/functions";
const queue = getFunctions().taskQueue(
`locations/${process.env.FUNCTION_REGION}/functions/syncBigQuery`
);
await queue.enqueue(taskData);
Se a chamada de enfileiramento segmentar uma base de código com prefixo, o nome da função descoberta também terá um prefixo (por exemplo, orders-syncBigQuery). Consulte Analisar e instalar a instância do kit de função de substituição e Testar como um kit de função.
6. Declarar as APIs e os papéis do IAM necessários
Mova os requisitos de IAM e API da extensão de extension.yaml para o
código:
import { requiresAPI, requiresRole } from "firebase-functions";
requiresAPI("bigquery.googleapis.com", "Needed to write changelog rows");
requiresRole("roles/bigquery.dataEditor");
requiresRole("roles/bigquery.user");
Com a segurança declarativa, a CLI Firebase cria ou atualiza uma conta de serviço de tempo de execução gerenciado para a base de código e concede a ela a união de todos os papéis declarados. Documente para seus usuários que todas as funções no codebase são executadas com essas funções, a menos que a API final seja compatível com um modelo mais restrito.
Exemplo prático:transmissão de Cloud Firestore para BigQuery
Antes. Declarado em extension.yaml. O ambiente de execução das extensões ativou a
API e concedeu os papéis a uma conta gerenciada:
apis:
- apiName: bigquery.googleapis.com
roles:
- role: bigquery.dataEditor
- role: datastore.user
- role: bigquery.user
Depois. Declarado no código com requiresAPI e requiresRole:
import { requiresAPI, requiresRole } from "firebase-functions";
requiresAPI(
"bigquery.googleapis.com",
"Needed to write changelog rows and views"
);
requiresRole("roles/bigquery.dataEditor");
requiresRole("roles/datastore.user");
requiresRole("roles/bigquery.user");
Se a extensão publicar eventos do
Eventarc, você também precisará
definir as APIs e os papéis obrigatórios adequados para publicar eventos. Antes, isso era processado pelas extensões sem que fosse necessário fazer mudanças no seu extensions.yaml. Você pode fazer isso de forma condicional no seu código para que o kit
só solicite essas permissões quando um canal personalizado do Eventarc for usado.
if (!!process.env.EVENTARC_CHANNEL) {
requiresRole("roles/eventarc.publisher");
requiresAPI(
"eventarcpublishing.googleapis.com",
"Publishes the extension's custom events to its Eventarc channel."
);
}
7. Converter hooks de ciclo de vida
Se a extensão chamar getExtensions().runtime() (por exemplo,
setProcessingState ou setFatalError), exclua essas chamadas, porque elas geram um
erro se forem chamadas de uma função de 2ª geração implantada normalmente. O estado do ciclo de vida agora é controlado por afterFirstDeploy e afterRedeploy, em que esse rastreamento de estado não é usado.
O Firebase Extensions pode executar a configuração quando um usuário instala, atualiza ou reconfigura uma extensão. No pacote npm, declare ações equivalentes do ciclo de vida no código.
Para uma configuração única:
import { afterFirstDeploy } from "firebase-functions/lifecycle";
import { onTaskDispatched } from "firebase-functions/tasks";
export const runInitialSetup = onTaskDispatched(async (request) => {
await initializeResources(request.data);
});
afterFirstDeploy({
task: {
function: "runInitialSetup",
body: {}
}
});
Para atualizações de configuração ou código:
import { afterRedeploy } from "firebase-functions/lifecycle";
afterRedeploy({
task: {
function: "runInitialSetup",
body: { reconcile: true }
}
});
Torne as ações de ciclo de vida idempotentes. Seus usuários talvez precisem executar esses comandos manualmente se o envio ou a execução falhar:
firebase functions:lifecycle:run afterFirstDeploy CODEBASE_NAME
firebase functions:lifecycle:run afterRedeploy CODEBASE_NAME
Exemplo prático:transmissão de Cloud Firestore para BigQuery
Antes. lifecycleEvents em extension.yaml, causado pelo tempo de execução
das extensões:
lifecycleEvents:
onInstall:
function: initBigQuerySync
processingMessage: Configuring BigQuery Sync.
onUpdate:
function: setupBigQuerySync
processingMessage: Configuring BigQuery Sync
onConfigure:
function: setupBigQuerySync
processingMessage: Configuring BigQuery Sync
Depois. Declarado no código, a tarefa provisiona BigQuery na primeira implantação:
import { afterFirstDeploy, afterRedeploy } from "firebase-functions/lifecycle";
afterFirstDeploy({ task: { function: "initBigQuerySync" } });
afterRedeploy({ task: { function: "setupBigQuerySync" } });
O provisionamento é idempotente. Portanto, uma nova execução reconcilia o conjunto de dados, a tabela e as visualizações.
Os usuários podem executar novamente de forma manual com firebase functions:lifecycle:run afterFirstDeploy
CODEBASE_NAME.
8. Documentar a configuração para os usuários
Escreva um pacote README que explique, no mínimo:
- Os valores de
.envque o pacote exige. - Os secrets que o pacote exige e como migrar valores de secret existentes.
- Os papéis do IAM que o pacote declara com
requiresRole(...). - As APIs do Google que o pacote ativa ou exige.
- Os hooks de ciclo de vida declarados pelo pacote e como executá-los novamente manualmente.
- Observações sobre o faturamento.
- O que mudou em comparação com a extensão original.
- Como o pacote recebe o ID da instância (
FIREBASE_KIT_INSTANCE_IDdefinido pela CLI) e que todas as funções da instância são implantadas com um prefixokit-<instanceId>-.
Exemplo prático:transmissão de Cloud Firestore para BigQuery
O pacote README envia uma tabela concreta de "o que mudou":
| Problema | Como a extensão | Como @firebase-function-kits/firestore-bigquery-export |
|---|---|---|
| Configuração | Parâmetros de extensão | Parâmetros Cloud Functions via .env |
| IAM | Concedida por extensões | requiresRole(...), aplicada na implantação |
| Provisionamento | Tarefa de ciclo de vida por extensões | afterFirstDeploy / afterRedeploy tarefa |
| Nomes de funções | ext-<instanceId>-fsexportbigquery |
fsexportbigquery (com prefixo opcional) |
| ID da instância | EXT_INSTANCE_ID injetado por extensões |
FIREBASE_KIT_INSTANCE_ID, definido pela CLI de firebase.json |
9. Testar a função de 2ª geração
Agora você tem uma função de 2ª geração que, quando implantada, se comporta de maneira idêntica a uma nova instalação da sua extensão. A próxima etapa é verificar e corrigir problemas introduzidos acidentalmente ao longo do processo.
Para chamar setGlobalOptions
e definir opções globais, como uma região ou CPU padrão, faça isso apenas ao
implantar seu kit como uma função independente de 2ª geração. Quando o kit é instalado
como um pacote npm, os usuários chamam setGlobalOptions no código de encapsulamento para
configurar esses parâmetros e recebem avisos se isso acontecer duas vezes.
É possível proteger essa chamada verificando a variável de ambiente FIREBASE_KIT_INSTANCE_ID:
import { setGlobalOptions } from "firebase-functions";
if (!process.env.FIREBASE_KIT_INSTANCE_ID) {
setGlobalOptions({
region: "us-east1",
maxInstances: 10,
});
}
Verifique se você está usando
firebase-tools
>= 15.32.0 e implante a função convertida de 2ª geração em um projeto de teste
com os recursos adequados para testar o comportamento dela. Se você já tiver um projeto de teste
configurado para testar sua extensão, execute o seguinte comando:
firebase deploy --only functions
Preencha o assistente resultante solicitando valores de parâmetro da mesma forma que você preencheria o formulário de instalação no console Firebase para a extensão.
Exemplo prático:transmissão de Cloud Firestore para BigQuery
Verificamos a sincronização de ponta a ponta de Cloud Firestore para BigQuery:
- Na página Cloud Firestore do console Firebase, crie a coleção
que você definiu como
COLLECTION_PATH(users) se ela ainda não existir. - Crie um documento chamado
bigquery-mirror-testcom campos e valores. Na página BigQuery do console Google Cloud, consulte a tabela de mudanças bruta. Ele precisa conter uma única linha registrando a criação do documento:
SELECT * FROM `PROJECT_ID.analytics.users_raw_changelog`Consulte a visualização mais recente, que vai retornar o evento de mudança mais recente do único documento presente (
bigquery-mirror-test):SELECT * FROM `PROJECT_ID.analytics.users_raw_latest`Exclua o documento
bigquery-mirror-testem Cloud Firestore. Ele desaparece da visualização mais recente, e um eventoDELETEé anexado à tabela bruta do changelog.É possível inspecionar o histórico completo de um único documento com:
SELECT * FROM `PROJECT_ID.analytics.users_raw_changelog` WHERE document_name = "bigquery-mirror-test" ORDER BY timestamp ASC
Diferenças em relação ao teste da extensão:
- O gatilho é implantado como
fsexportbigquery(sem prefixo ao implantar como uma função típica de 2ª geração e não um kit), nãoext-<instanceId>-fsexportbigquery. Procure esse nome no painel e nos registros de Cloud Functions. - Agora seu código é executado no Firebase Local Emulator Suite como funções normais.
É possível definir o valor dos parâmetros a serem usados no emulador com
.env.local. Também é possível testar seu código usando o SDKfirebase-functions-test, conforme descrito em Teste de unidade de Cloud Functions. - O provisionamento não é mais feito pelo tempo de execução das extensões. Se a tabela de
changelog estiver faltando após a implantação, execute novamente a tarefa de configuração manualmente:
firebase functions:lifecycle:run afterFirstDeploy CODEBASE_NAME. A tarefa é idempotente. Portanto, ao executá-la novamente, o conjunto de dados, a tabela e as visualizações são reconciliados. - Os valores de parâmetro vêm de
.envem vez do formulário de instalação. Portanto, as novas execuções defirebase deploynão são interativas depois que.envé concluído.
10. Publicar o kit de funções no npm
Depois de validar a conversão da extensão para a função de 2ª geração, você pode publicar uma versão candidata a lançamento no npm para testes de ponta a ponta usando um dos seguintes guias:
Quando o kit é publicado no npm, ele pode ser instalado com
firebase functions:kits:install e listado como o
substituto oficial da sua extensão.
Recomendamos publicar primeiro uma versão candidata a lançamento. Os kits são instalados por
nome e versão do pacote. Assim, uma prévia permite testar o fluxo de instalação
real no registro sem expor um pacote inacabado aos usuários que
instalam com a tag latest padrão.
Antes de publicar:
- Escolha um nome de pacote. Os nomes com e sem escopo funcionam. Consulte os guias acima. Os pacotes com escopo são particulares por padrão. Portanto, transmita
--access public. - Crie e verifique o que é enviado.
mainetypesapontam para a saída compilada (lib/no exemplo trabalhado). Portanto, esse diretório precisa ser incluído no arquivo tar publicado. Use.npmignoreou uma lista de permissõesfilese inspecione o resultado comnpm pack --dry-run. Um scriptprepublishOnlyque executa sua build impede a publicação de resultados desatualizados.
package.json adições que tornam a publicação segura por padrão:
{
"files": ["lib", "README.md", "CHANGELOG.md"],
"publishConfig": { "access": "public", "tag": "next" },
"scripts": {
"build": "tsc -b",
"prepublishOnly": "npm run build && npm test"
}
}
O campo "publishConfig": { "tag": "next" } garante que um npm
publish simples nunca substitua latest.
Criação de uma versão candidata a lançamento:
Por exemplo, para incrementar a versão localmente de 0.0.2-rc.3 para 0.0.2-rc.4
(isso confirma e marca no Git se package.json estiver na raiz do repositório):
npm version prerelease --preid rc
Para publicar a versão candidata a lançamento e registrar @your-org/your-kit@0.0.2-rc.4 no
npm com a tag next:
npm publish
O site do npm pode levar alguns minutos para mostrar a nova versão. O npm view lê o registro diretamente:
npm view @your-org/your-kit versions dist-tags
Depois de concluir as etapas 11 e 12 deste guia, você poderá promover o pacote para uma versão estável:
npm version 0.0.2
npm publish --tag latest
npm dist-tag add @your-org/your-kit@0.0.2 next
Observação sobre npm-shrinkwrap.json:recomendamos incluir um arquivo
npm-shrinkwrap.json com seu pacote. A CLI avisa os usuários na instalação se
você não fizer isso. Ele garante que os usuários usem as dependências exatas que você testou e ajuda a proteger contra ataques à cadeia de suprimentos. No entanto, um shrinkwrap é aplicado
de forma literal nos projetos dos usuários, inclusive durante o build do Cloud Functions
(npm ci), em que entradas somente para desenvolvimento podem falhar com EBADPLATFORM. Talvez seja necessário
remover entradas "dev": true e devDependencies da cópia
publicada do shrinkwrap.
Exemplo prático:transmissão de Cloud Firestore para BigQuery
O package.json do kit no momento da quarta versão candidata a lançamento:
{
"name": "@firebase-function-kits/firestore-bigquery-export",
"version": "0.0.2-rc.4",
"repository": {
"type": "git",
"url": "https://github.com/firebase/extensions.git",
"directory": "kits/firestore-bigquery-export"
},
"main": "lib/index.js",
"types": "lib/index.d.ts",
"engines": { "node": "22" },
"scripts": { "build": "tsc -b" }
}
Nesse caso, o kit está em um monorepo. Por isso, é importante incluir repository.directory para que o link do registro npm aponte para a pasta correta. O CHANGELOG.md dele contém notas sobre o lançamento pendente.
11. Teste como um kit de funções
Depois de publicar o kit, recomendamos testá-lo usando o npm.
Verifique se você está usando a
versão >= 15.32.0 do
firebase-tools
e instale o kit:
firebase functions:kits:install --package <your-package-name>@<your-prerelease-version>
Isso faz o download do pacote do npm, configura-o em um novo diretório de origem para seu kit e orienta você na configuração da primeira instância, semelhante ao fluxo de instalação de extensões. Depois de instalar e configurar o pacote localmente, execute uma implantação para criar os recursos no projeto Google Cloud:
firebase deploy --only functions:<your-kit-instance-id>
Após a instalação, a CLI Firebase vai imprimir um comando de implantação semelhante com o ID exato da instância escolhida durante a instalação.
Valide o kit novamente seguindo as instruções em
Etapa 9. Teste a função de 2ª geração. Agora que você está
implantando usando kits, suas funções são prefixadas e nomeadas
kit-<instance-id>-<method-name>. Isso permite que os kits tenham várias instâncias,
implantando a mesma função várias vezes em um projeto, cada uma com um nome
exclusivo.
12. Teste de substituição de migração
É possível configurar uma instância de extensão funcional e usar o
guia de migração para usuários
(usando firebase ext:migrate --package
ou os comandos da CLI dos kits de funções)
para concluir o teste do kit de funções como uma substituição de migração.
13. Notificar usuários e o Google sobre a substituição oficial da extensão
Quando a substituição do kit de funções estiver pronta e disponível como um pacote npm para
migração dos usuários, informe a eles e ao Google sobre essa substituição
oficial. Atualize o arquivo README.md no repositório do GitHub que hospeda sua extensão com as seguintes informações:
<!-- FIREBASE_EXTENSION_REPLACEMENT: extension="<your-extesion-id>" package="<your-npm-package-name>" -->
> [!WARNING]
> **Deprecation Notice:** The Firebase Extension `<your-extension>` is deprecated. Migrate to the [<your-npm-package-name>](<link-to-your-npm-package>) package.
O Google verifica os READMEs de extensões conhecidas em busca de comentários como <!--
FIREBASE_EXTENSION_REPLACEMENT: extension="firebase/firestore-bigquery-export"
package="@firebase-function-kits/firestore-bigquery-export" --> e usa isso para preencher nosso registro oficial de substituições armazenado no repositório firebase-tools como replacements.json.
Você também pode verificar replacements.json para saber quais README.md serão verificados
na sua extensão. A lista oficial de substituições é atualizada semanalmente.
Exemplo prático:transmissão de Cloud Firestore para BigQuery
A extensão firestore-bigquery-export
README.md
contém:
<!-- FIREBASE_EXTENSION_REPLACEMENT: extension="firebase/firestore-bigquery-export" package="@firebase-function-kits/firestore-bigquery-export" -->
> [!WARNING]
> **Deprecation Notice:** The Firebase Extension `firebase/firestore-bigquery-export` is deprecated. Please migrate to the [`@firebase-function-kits/firestore-bigquery-export`](https://www.npmjs.com/package/@firebase-function-kits/firestore-bigquery-export) package.