Ir a la consola

Guía de actualización del SDK de Firebase para Cloud Functions: de la versión Beta a la versión 1.0 o posterior

La versión 1.0.0 del SDK de Firebase para Cloud Functions incluyó cambios importantes en la API. El cambio principal, el reemplazo del formato event.data por los parámetros datacontext, afecta a todas las funciones asíncronas (que no son HTTP). Además, el SDK actualizado se puede usar con firebase-functions-test, un SDK complementario para las pruebas de unidades. Consulta Funciones de pruebas de unidades a fin de obtener más información.

La versión 2.0.0 del SDK de Firebase para Cloud Functions incluyó un cambio rotundo en las marcas de tiempo de las funciones que se activan con Firestore.

Para actualizar los SDK a la versión más reciente, ejecuta los siguientes comandos en la carpeta de funciones:

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

También deberías actualizar Firebase CLI a su versión más reciente:

npm install -g firebase-tools

Los cambios del SDK que afectan a todas las funciones asíncronas (que no son HTTP) son los siguientes:

Cambios de SDK por tipo de activador

Referencias de API nuevas y heredadas

Cambios en la emulación de funciones

Cambios del SDK que afectan a todas las funciones en segundo plano (que no son HTTP)

Parámetro del evento dividido en datos y contexto

A partir de la versión 1.0 del SDK de Firebase para Cloud Functions, el parámetro event para las funciones asíncronas es obsoleto. Se reemplazó por dos nuevos parámetros: data y context.

El parámetro data representa los datos que activaron la función. Los campos del parámetro data están determinados por el tipo de activador y varían de acuerdo con ello. Por ejemplo, para Realtime Database, el parámetro data es una DataSnapshot. Consulta los cambios por tipo de activador para obtener más información sobre el parámetro data.

El parámetro context proporciona información sobre la ejecución de la función. context contiene los campos eventId, timestamp, eventType, resource y params, y es idéntico en todos los tipos de funciones asíncronas. Además, las funciones de Realtime Database ofrecen datos de autenticación al usuario que las activó. A continuación, se muestra un ejemplo de los campos de contexto definidos en una función activada por una escritura en 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.
  // ...
});

Nueva sintaxis de inicialización para firebase-admin

firebase-admin ahora se inicializa sin parámetros en el tiempo de ejecución de Cloud Functions.

Antes (<= v0.9.1)

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

Ahora (> = v1.0.0)

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

Ten en cuenta que ya no puedes pasar functions.config().firebase durante la inicialización. Consulta la sección siguiente para obtener detalles sobre cómo acceder a la configuración en la versión 1.0.0.

Se quitó functions.config().firebase

Se quitó functions.config().firebase. Si quieres acceder a los valores de configuración desde tu proyecto de Firebase, usa process.env.FIREBASE_CONFIG en su lugar:

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

Cambios de SDK por tipo de activador

La versión 1.0 incluye cambios en la nomenclatura de los campos de datos y de los métodos para muchos activadores de funciones compatibles. La lista de cambios por tipo de activador se incluye en esta sección.

Realtime Database

Los datos de eventos ahora son DataSnapshot

En versiones anteriores, event.data era una DeltaSnapshot; en la versión 1.0, es una DataSnapshot.

Para los eventos onWrite y onUpdate, el parámetro de datos tiene los campos before y after. Cada uno de estos es una DataSnapshot con los mismos métodos disponibles en admin.database.DataSnapshot. Por ejemplo:

Antes (<= v0.9.1)

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

Ahora (>= v1.0.0)

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, el parámetro de datos es una DataSnapshot que representa los datos que se agregaron:

Antes (<= v0.9.1)

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

Ahora (> = v1.0.0)

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

Para onDelete, el parámetro de datos es una DataSnapshot que representa los datos que se borraron:

Antes (<= v0.9.1)

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

Ahora (> = v1.0.0)

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

Propiedades nuevas de la información de autenticación del usuario

La versión 1.0.0 de EventContext.auth incluye dos propiedades nuevas para acceder a la información del usuario que activó una función, incluidos los permisos.

  • EventContext.auth. Incluye información como uid y el token de autenticación del usuario autenticado.
  • EventContext.authType. Incluye niveles de permisos, lo que permite detectar si el usuario es un usuario admin (entre otros).

Para usar estas propiedades nuevas, los desarrolladores que usen los campos event.auth no documentados deben actualizar los códigos relacionados.

adminRef se reemplazó por ref

Se quitó la referencia .adminRef para dar lugar a la referencia .ref, que ahora cuenta con privilegios de administrador. Ya no se admite el uso anterior de .ref, como referencia del cambio autorizado como el usuario que activó el cambio.

Antes (<= v0.9.1)

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

Ahora (> = v1.0.0)

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

Aún se pueden realizar cambios autorizados por el usuario en la Realtime Database mediante el SDK de 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

De la misma manera que los cambios en la Realtime Database para la versión 1.0, onWrite y onUpdate tienen un parámetro de datos con los campos before y after. Los eventos para onCreate y onDelete tienen un parámetro de datos que es una DocumentSnapshot de Cloud Firestore.

Antes (<= v0.9.1)

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

Ahora (>= v1.0.0)

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

Los eventos para onCreate y onDelete tienen un parámetro de datos que es una DocumentSnapshot.

Antes (<= v0.9.1)

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

Ahora (>= v1.0.0)

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

Cambio importante en la versión v2.0.0 de las marcas de tiempo de Firestore

En la versión 2.0.0 del SDK de Firebase para Cloud Functions, los valores de marca de tiempo de una instantánea de Firestore recibidos dentro de una función son objetos de marca de tiempo de Firestore. Esto se aplica a snapshot.createTime, snapshot.updateTime, snapshot.readTime y a cualquier valor de marca de tiempo en snapshot.data().

Ahora (> = v2.0.0)

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

Autenticación

En versiones anteriores, event.data.metadata incluía los campos obsoletos createdAt y lastSignedInAt. La versión 1.0 quita estos campos del todo y los reemplaza por creationTime y lastSignInTime en el parámetro userRecord.metadata.

Antes (<= v0.9.1)

// 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
}

Ahora (> = v1.0.0)

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

En la versión 1.0, el controlador de eventos que se activa cada vez que se produce un error es onNew. Se quitó el controlador anterior onNewDetected.

Antes (<= v0.9.1)

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;
}

Ahora (> = v1.0.0)

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

Se quitó el controlador de eventos onChange. En su lugar, la versión 1.0 admite los eventos siguientes:

  • onArchive: Solo se envía cuando un depósito habilita el control de versiones de objetos. Este evento señala que la versión publicada de un objeto se convirtió en una versión archivada, ya sea porque se la archivó o porque se reemplazó cuando se subió un objeto con el mismo nombre.
  • onDelete: Se envía cuando un objeto se borra permanentemente. Incluye los objetos que se sobrescriben o que se borran según la configuración del ciclo de vida del depósito. En el caso de los depósitos que tienen habilitado el control de versiones de objetos, no se envía cuando se archiva un objeto (consulta onArchive), incluso si se realiza el archivado con el método storage.objects.delete.
  • onFinalize: Se envía cuando un nuevo objeto (o una nueva generación de un objeto existente) se crea correctamente en el depósito. Incluye la copia y la reescritura de un objeto existente. Una carga con errores no activa este evento.
  • onMetadataUpdate: Se envía cuando cambian los metadatos de un objeto existente.

Antes (<= v0.9.1)

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;
  }

  // ...
}

Ahora (>= v1.0.0)

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

Debes cambiar el nombre y volver a implementar las funciones Pub/Sub, ya que cambió el tipo de activador subyacente. No es necesario cambiar el código de las funciones.

Para actualizar las funciones Pub/Sub, haz lo siguiente:

  1. Cambia el nombre de la función. Por ejemplo, cambia el nombre de "myPubSubFunction" a "myNewPubSubFunction".
  2. Implementa solo esa función mediante una implementación parcial:

    firebase deploy --only functions:myNewPubSubFunction

  3. Cuando hayas implementado "myNewPubSubFunction" correctamente, borra la función obsoleta anterior a la versión 1.0.0. Para ello, implementa todas las funciones:

    firebase deploy --only functions

Durante el período breve entre ambos comandos de implementación, podrías recibir activadores duplicados. Para manejar este caso y garantizar una operación normal, asegúrate de escribir funciones idempotentes.

Referencias de API nuevas y heredadas

Si deseas obtener más información, consulta la referencia de la API para firebase-functions v1.0.0. También puedes consultar la referencia de la API para la versión 0.9.1 y obtener ayuda con la actualización.

Cambios en la emulación de funciones

  • firebase serve ahora proporciona funciones HTTP y hosting de forma predeterminada.
  • firebase experimental:functions:shell, que emula todas las funciones, ahora se llama firebase functions:shell.

Cambios en la sintaxis del shell de Functions

Se actualizó la sintaxis para invocar funciones con el shell de Functions con el fin de reflejar la sintaxis de 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’)