This article provides a complete breakdown of an exciting and widely anticipated new feature called Lifecycle events. Here we explore what, when, and how to use them.
Introduction
Lifecycle events are new types of hook
, explicitly developed for Firebase Extensions. They allow pre-defined code to run based on the following events:
- onInstall: This occurs when a Firebase Extension has been installed for the first time.
- onUpdate: This occurs start whenever a Firebase Extension has been updated. This could be when updating to a newer through the Firebase console or updating an existing installation through the `cli`.
- onConfigure: This occurs when a Firebase Extension has been updated in the console, and the `Save` button has been selected to reconfigure.
Why Lifecycle events?
To use these types of events, it is essential to understand the history and use cases as to why this type of functionality is useful.
Thinking beyond triggers
Before events, Firebase Extensions could interact with two sources:
- Calling functions directly through HTTP Requests
- Background triggers (such as Firestore and Authentication triggers)
Allowing users to provide configuration when installing/configuring the extension, functions could be created for specific config. Once installed, the functions are triggered based on the settings, which works great!
But what if we wanted to run some configuration beforehand? Or what if the Extension had been installed with existing data? Tools such as the `import` script for BigQuery, or manual updates on each translation document demanded custom/maintainable solutions.
The Stream Collections to BigQuery extension, is an excellent example of this, as the Extension demands that the tables and views exist before syncing any data.
This means the table must be checked/created/updated on every record write; the new events can be done once the extension is installed/reconfigured.
A new type of event
A function would be required for each event type to allow full customization.
By allowing custom functions to be defined by developers, fully customized functions could be run before the installation has been completed. This would be required to pause any processing from the Extensions triggers until the events have been completed.
Additionally, as these are still functions and we have the old-age problems of memory
and timeout
issues. This solution would require Cloud Tasks to ensure fast and well-distributed processing for scenarios with high levels of processing and data.
Tracking progress
Ok, so this all sounds great. We now have a way to run tasks based on the initial/reconfiguration of extensions. We can even run custom updates when updating/downgrading to different versions of an extension.
We must also consider how to allow users/developers to track the progress of these tasks, mainly if they will take a long time to process. Ant errors/status updates would also be presented.
Getting started
The new features we will use as part of these updates depend upon recent SDK updates.
"firebase-admin": "^11.3.0",
"firebase-functions": "^3.24.0",
Extension.yaml
The Extension configuration file also requires updates to define which events should run.
lifecycleEvents:
onInstall:
function: {fn_name}
processingMessage: {your_message}
onUpdate:
function: {fn_name}
processingMessage: {your_message}
onConfigure:
function: {fn_name}
processingMessage: {your_message}
Any of the three event types can be defined and must contain two properties:
function:
The name of the exported function in your index.ts
file. This must be a `Cloud task`, for example:
export const myFn = functions.tasks
.taskQueue()
.onDispatch(async (data) => {}
processingMessage:
The initial
starting message that will be used on the extension `runtime status` console.
Batch processing
Cloud tasks provide a feature for queuing tasks. We can hook into the Extension queue itself and add another task via
/* Find the current task instance for the Extension */
const queue = getFunctions().taskQueue(
`backfillResizedImages`,
process.env.EXT_INSTANCE_ID
);
And then add another task to the queue
await queue.enqueue({/* add property data */});
Completing an event
Once we have finished processing, the function can return to finish the task.
/*completed processing*/
return;
Realtime status updates
As noted in the `Runtime status` panel above, we initially set a message when defining the event through the configuration file.
This can be updated through our functions by setting the current processing message.
await runtime.setProcessingState(
"{status}",
"{message}"
);
The processing state requires two variables:
Status: This is the text that appears as the title of the status. This could be “PROCESSING”, “PROCESSING COMPLETE” for example.
Message: This is the text that appears in the main body of the status box.
System/Configuration Variables
Any extension development relies on accessing environmental variables; with lifecycle events, this is no exception.
All config and system variables are made available and are available for developers.
Examples:
You can include variables in the runtime status when configuring an event. The following will include the location of your function, for example:
onInstall:
function: {fn_name}
processingMessage: `Running onInstall from ${param:LOCATION}`
Setting the processing state will state the Image bucket that you are running from in this example:
await runtime.setProcessingState(
"PROCESSING",
"Resizing existing images in ${param:IMG_BUCKET}"
);
Conclusion
Lifecycle events open up a new world for defining Firebase Extensions without repeating code and logic in Triggers and HTTP events.
This allows much cleaner, maintainable, and performance-based enhancements for developing your Extensions.
The first two significant examples can be found in the latest release of Storage Resize Images (0.1.32) and Firestore Translate Text (0.1.9).
Check out the source code here, and we look forward to seeing how developers use this new feature!
Stay tuned for more updates and exciting news that we will share in the future. Follow us on Invertase Twitter, Linkedin, and Youtube, and subscribe to our monthly newsletter to stay up-to-date. You may also join our Discord to have an instant conversation with us.