Guia de atualização do SDK do Firebase para Cloud Functions: da versão Beta à 1.0 ou posterior

A versão 1.0.0 do SDK do Firebase para Cloud Functions introduziu algumas alterações importantes na API. A alteração principal, uma substituição do formato event.data por parâmetros data e context, afeta todas as funções assíncronas (não HTTP). O SDK atualizado também pode ser usado com o firebase-functions-test, um SDK complementar de teste de unidade. Consulte Funções de teste de unidade para mais informações.

A versão 2.0.0 do SDK do Firebase para Cloud Functions apresentou uma alteração importante nos carimbos de data/hora em funções acionadas pelo Firestore.

Para atualizar os SDKs para a versão mais recente, execute o seguinte comando na pasta de funções:

npm install firebase-functions@latest --save
npm install firebase-admin@latest --save-exact

Também é preciso atualizar a Firebase CLI para a versão mais recente:

npm install -g firebase-tools

Alterações do SDK que afetam todas as funções assíncronas (não HTTP)

Alterações do SDK por tipo de acionador

Alterações na emulação de funções

Alterações do SDK que afetam todas as funções em segundo plano (não HTTP)

Divisão de parâmetros de eventos em dados e contexto

A partir da v1.0 do SDK do Firebase para Cloud Functions, o parâmetro event para funções assíncronas está obsoleto. Ele foi substituído por dois novos parâmetros: data e context.

O parâmetro data representa os dados que acionaram a função. Os campos do parâmetro data são determinados pelo tipo de gatilho e variam de acordo. Por exemplo, para o Realtime Database, o parâmetro data é um DataSnapshot. Consulte alterações por tipo de gatilho para mais informações sobre o parâmetro data.

O parâmetro context fornece informações sobre a execução da função. Idêntico em todos os tipos de funções assíncronas, context contém os campos eventId, timestamp, eventType, resource e params. Além disso, as funções do Realtime Database fornecem informações de autenticação ao usuário que acionou a função. Segue um exemplo de campos de contexto definidos em uma função acionada por uma gravação no Realtime Database:

exports.dbWrite = functions.database.ref('/path/with/{id}').onWrite((data, context) => {
  const authVar = context.auth; // Auth information for the user.
  const authType = context.authType; // Permissions level for the user.
  const pathId = context.params.id; // The ID in the Path.
  const eventId = context.eventId; // A unique event ID.
  const timestamp = context.timestamp; // The timestamp at which the event happened.
  const eventType = context.eventType; // The type of the event that triggered this function.
  const resource = context.resource; // The resource which triggered the event.
  // ...
});

Nova sintaxe de inicialização para firebase-admin

firebase-admin agora é inicializado sem parâmetros no tempo de execução do Cloud Functions.

Versão v0.9.1 e anteriores

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);

Versão v1.0.0 e posteriores

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();

Não é mais possível transmitir functions.config().firebase ao inicializar. Consulte a seção a seguir para mais detalhes sobre como acessar a configuração na v1.0.0.

Contato functions.config().firebase removido

functions.config().firebase foi removido. Se você quiser acessar os valores de configuração do seu projeto do Firebase, use process.env.FIREBASE_CONFIG:

let firebaseConfig = JSON.parse(process.env.FIREBASE_CONFIG);
/* {  databaseURL: 'https://databaseName.firebaseio.com',
       storageBucket: 'projectId.appspot.com',
       projectId: 'projectId' }
*/

Alterações do SDK por tipo de gatilho

Para muitos gatilhos de função compatíveis, a versão 1.0 apresentou alterações nos nomes de campos e métodos de dados. Veja nesta seção uma lista por tipo de acionador.

Realtime Database

Os dados do evento agora são DataSnapshot

Em versões anteriores, event.data era um DeltaSnapshot. A partir da v1.0, é um DataSnapshot.

Para eventos onWrite e onUpdate, o parâmetro de dados tem os campos before e after. Cada um deles é um DataSnapshot com os mesmos métodos disponíveis em admin.database.DataSnapshot. Exemplo:

Versão v0.9.1 e anteriores

exports.dbWrite = functions.database.ref('/path').onWrite((event) => {
  const beforeData = event.data.previous.val(); // data before the write
  const afterData = event.data.val(); // data after the write
});

Versão v1.0.0 e posteriores

exports.dbWrite = functions.database.ref('/path').onWrite((change, context) => {
  const beforeData = change.before.val(); // data before the write
  const afterData = change.after.val(); // data after the write
});

Para onCreate, o parâmetro de dados é um DataSnapshot que representa os dados que acabaram de ser adicionados:

Versão v0.9.1 e anteriores

exports.dbCreate = functions.database.ref('/path').onCreate((event) => {
  const createdData = event.data.val(); // data that was created
});

Versão v1.0.0 e posteriores

exports.dbCreate = functions.database.ref('/path').onCreate((snap, context) => {
  const createdData = snap.val(); // data that was created
});

Para onDelete, o parâmetro de dados é um DataSnapshot que representa os dados que acabaram de ser excluídos:

Versão v0.9.1 e anteriores

exports.dbDelete = functions.database.ref('/path').onDelete((event) => {
  const deletedData = event.data.previous.val(); // data that was deleted
});

Versão v1.0.0 e posteriores

exports.dbDelete = functions.database.ref('/path').onDelete((snap, context) => {
  const deletedData = snap.val(); // data that was deleted
});

Novas propriedades para informações de autenticação de usuário

O EventContext.auth V1.0.0 introduziu duas novas propriedades para acessar informações do usuário, incluindo permissões, para o usuário que acionou uma função.

  • EventContext.auth. Contém informações, como uid e o token de autenticação do usuário autenticado.
  • EventContext.authType. Contém níveis de permissão, o que permite detectar se o usuário é um usuário administrador, por exemplo.

Os desenvolvedores que usam os campos event.auth não documentados precisam atualizar qualquer código relacionado para usar essas novas propriedades.

Substituição de adminRef por ref

A referência .adminRef foi removida em favor da referência .ref que agora está autorizada com privilégios de administrador. A primeira maneira de usar .ref, como referência à alteração autorizada como o usuário que acionou a alteração, não é mais compatível.

Versão v0.9.1 e anteriores

exports.dbCreate = functions.database.ref('/path/{uid}').onCreate((event) => {
  const parentRef = event.data.adminRef.parent; // The Database reference to the parent authorized with admin privileges.

  const parentRefAsUser = event.data.ref.parent; // The Database reference to the parent authorized as the user which triggered the change.
});

Versão v1.0.0 e posteriores

exports.dbCreate = functions.database.ref('/path/{uid}').onCreate((snap, context) => {
  const parentRef = snap.ref.parent; // The Database reference to the parent authorized with admin privileges
});

Você ainda pode aplicar alterações autorizadas pelo usuário no Realtime Database usando o SDK Admin:

const functions = require('firebase-functions');
const admin = require('firebase-admin');

exports.impersonateMakeUpperCase = functions.database.ref('/messages/{pushId}/original')
    .onCreate((snap, context) => {
      const appOptions = JSON.parse(process.env.FIREBASE_CONFIG);
      appOptions.databaseAuthVariableOverride = context.auth;
      const app = admin.initializeApp(appOptions, 'app');
      const uppercase = snap.val().toUpperCase();
      const ref = snap.ref.parent.child('uppercase');

      const deleteApp = () => app.delete().catch(() => null);

      return app.database().ref(ref).set(uppercase).then(res => {
        // Deleting the app is necessary for preventing concurrency leaks
        return deleteApp().then(() => res);
      }).catch(err => {
        return deleteApp().then(() => Promise.reject(err));
      });
    });

Cloud Firestore

Assim como as alterações do Realtime Database para v1.0, onWrite e onUpdate têm um parâmetro de dados que tem os campos before e after. Os eventos para onCreate e onDelete têm um parâmetro de dados que é um DocumentSnapshot do Cloud Firestore.

Versão v0.9.1 e anteriores

exports.dbWrite = functions.firestore.document('/doc/path').onWrite((event) => {
  const beforeData = event.data.previous.data(); // data before the write
  const afterData = event.data.data(); // data after the write
});

Versão v1.0.0 e posteriores

exports.dbWrite = functions.firestore.document('/doc/path').onWrite((change, context) => {
  const beforeData = change.before.data(); // data before the write
  const afterData = change.after.data(); // data after the write
});

Os eventos para onCreate e onDelete têm um parâmetro de dados que é um DocumentSnapshot.

Versão v0.9.1 e anteriores

exports.dbDelete = functions.firestore.document('/doc/path').onDelete((event) => {
  const deletedData = event.data.previous.data(); // data that was deleted
});

Versão v1.0.0 e posteriores

exports.dbDelete = functions.firestore.document('/doc/path').onDelete((snap, context) => {
  const deletedData = snap.data(); // data that was deleted
});

Mudança importante na v2.0.0 para carimbos de data/hora do Firestore

A partir da v2.0.0 do SDK do Firebase para Cloud Functions, os valores de carimbo de data/hora em um instantâneo do Firestore recebido dentro de uma função são objetos de carimbo de data/hora do Firestore. Isso se aplica a snapshot.createTime, snapshot.updateTime, snapshot.readTime e a qualquer valor de carimbo de data/hora em snapshot.data()

Versão v2.0.0 ou posteriores

exports.dbCreate = functions.firestore.document('/doc/path').onCreate((snap, context) => {
  //seconds of UTC time since Unix epoch
  console.log(snap.createTime.seconds);

  //fractions of a second at nanosecond resolution, 0 to 999,999,999
  console.log(snap.createTime.nanoseconds);
});

Autenticação

Em versões anteriores, event.data.metadata continha os campos obsoletos createdAt e lastSignedInAt. A versão 1.0 remove esses campos completamente e os substitui pelos campos creationTime e lastSignInTime no parâmetro userRecord.metadata.

Versão v0.9.1 e anteriores

// This code won't work with Cloud Functions SDK 1.0 and higher!
exports.authAction = functions.auth.user().onCreate((event) => {
  const userMetadata = event.data.metadata;

  const creationTime = userMetadata.createdAt; // 2016-12-15T19:37:37.059Z
  const lastSignInTime = userMetadata.lastSignedInAt; // 2018-01-03T16:23:12.051Z
}

Versão v1.0.0 e posteriores

exports.authAction = functions.auth.user().onCreate((userRecord, context) => {
  const creationTime = userRecord.metadata.creationTime; // 2016-12-15T19:37:37.059Z
  const lastSignInTime = userRecord.metadata.lastSignInTime; // 2018-01-03T16:23:12.051Z
}

Crashlytics

Na versão 1.0, o manipulador de eventos que é disparado sempre que ocorre um novo problema é onNew. O gerenciador anterior chamado onNewDetected foi removido.

Versão v0.9.1 e anteriores

exports.newIssue = functions.crashlytics.issue().onNewDetected((event) => {
  const issue = event.data;

  const issueId = issue.issueId;
  const issueTitle = issue.issueTitle;
  const appName = issue.appInfo.appName;
  const appId = issue.appInfo.appId;
  const appPlatform = issue.appInfo.appPlatform;
  const latestAppVersion = issue.appInfo.latestAppVersion;
  const createTime = issue.createTime;
}

Versão v1.0.0 e posteriores

exports.newIssue = functions.crashlytics.issue().onNew((issue, context) => {
  const issueId = issue.issueId;
  const issueTitle = issue.issueTitle;
  const appName = issue.appInfo.appName;
  const appId = issue.appInfo.appId;
  const appPlatform = issue.appInfo.appPlatform;
  const latestAppVersion = issue.appInfo.latestAppVersion;
  const createTime = issue.createTime;
}

Storage

O manipulador de eventos onChange foi removido. Em vez dele, o v 1.0 oferece compatibilidade para estes eventos:

  • onArchive: enviado apenas depois que o controle de versões do objeto é ativado no intervalo. Este evento indica que a versão ativa de um objeto se tornou uma versão arquivada por ter sido arquivada ou substituída pelo upload de um objeto do mesmo nome.
  • onDelete: enviado quando um objeto é excluído permanentemente. Isso inclui objetos que são substituídos ou são excluídos como parte da configuração do ciclo de vida do intervalo. Nos buckets com o controle de versões de objetos ativado, ele não é enviado quando um objeto é arquivado (ver onArchive), mesmo que o arquivamento ocorra através do método storage.objects.delete.
  • onFinalize: enviado quando um novo objeto ou nova geração de um objeto existente é criada com sucesso no intervalo. Isso inclui copiar ou reescrever um objeto existente. Um upload com falha não aciona esse evento.
  • onMetadataUpdate: enviado quando os metadados de um objeto existente são alterados.

Versão v0.9.1 e anteriores

exports.processFile = functions.storage.object().onChange((event) => {
  const object = event.data;

  const filePath = object.name; // Path of the File
  const contentType = object.contentType; // Mime type of the file

  // Exit if this is a deletion event.
  if (object.resourceState === 'not_exists') {
    console.log('This file was deleted.');
    return null;
  }

  // Exit if file exists but is not new and is only being triggered
  // because of a metadata change.
  if (resourceState === 'exists' && metageneration > 1) {
    console.log('This is a metadata change event.');
    return null;
  }

  // ...
}

Versão v1.0.0 e posteriores

exports.processFile = functions.storage.object().onFinalize((object, context) => {
  const filePath = object.name; // Path of the File
  const contentType = object.contentType; // Mime type of the file

  // ...
}

exports.fileDeleted = functions.storage.object().onDelete((object, context) => {
  console.log('This file was deleted.');
}

exports.metadataUpdated = functions.storage.object().onMetadataUpdate((object, context) => {
  console.log('This is a metadata change event.');
}

Pub/Sub

Como o tipo de acionador subjacente foi alterado, você precisa renomear e reimplantar as funções do Cloud Pub/Sub. Não é necessária nenhuma alteração no código da função.

Para atualizar as funções do Cloud Pub/Sub, siga estas etapas:

  1. Renomeie a função. Por exemplo, renomeie "myPubSubFunction" para "myNewPubSubFunction".
  2. Implante apenas essa função usando uma implantação parcial:

    firebase deploy --only functions:myNewPubSubFunction

  3. Após implantar "myNewPubSubFunction", exclua a função obsoleta anterior à versão 1.0.0 implantando todas as funções:

    firebase deploy --only functions

Durante o breve período entre esses comandos de implantação, você pode receber acionadores duplicados. Para lidar com este caso e para garantir o funcionamento normal, escreva funções idempotentes.

Para saber mais, consulte a referência da API.

Alterações na emulação de funções

  • O firebase serve agora veicula funções HTTP e hospedagem por padrão.
  • firebase experimental:functions:shell, que emula todas as funções, foi renomeado para firebase functions:shell.

Mudanças de sintaxe para o shell de funções

A sintaxe para invocar funções por meio do shell de funções foi atualizada para refletir a sintaxe do firebase-functions v1.0.0+.

// Inside functions shell

// To emulate database writes done by an administrative user:
myDbFunction(‘data’)

// To emulate database writes done by an authenticated user:
myDbFunction(‘data’, { auth: { uid: ‘abc’ }})

// To emulate database writes done by an unauthenticated user:
myDbFunction(‘data’, { authMode: ‘UNAUTHENTICATED’)