1. Before you begin
A Firebase Extension performs a specific task or set of tasks in response to either HTTP requests or triggering events from other Firebase and Google products like Firebase Cloud Messaging, Cloud Firestore, or Pub/Sub.
What you'll build
In this codelab, you'll build a Firebase extension for geohashing. Once deployed, your extension then converts X- and Y-coordinates into geohashes in response to Firestore events or through callable function invocations. This can be used as an alternative to implementing the geofire library across all your target platforms for storing data, saving you time.
What you'll learn
- How to take existing Cloud Functions code and turn it into a distributable Firebase Extension
- How to set up an
extension.yaml
file - How to store sensitive strings (API keys) in an extension
- How to allow developers of the extension to configure it to suit their needs
- How to test and deploy the extension
What you'll need
- Firebase CLI (install and login)
- A Google account, like a gmail account
- Node.js and
npm
- Your favorite development environment
2. Get set up
Get the code
Everything you need for this extension is in a GitHub repo. To get started, grab the code and open it in your favorite development environment.
- Unpack the downloaded zip file.
- To install the required dependencies, open the terminal in the
functions
directory and run thenpm install
command.
Set up Firebase
This codelab highly encourages the use of Firebase emulators. If you want to try out extensions development with a real Firebase project, see create a Firebase project. This codelab uses Cloud Functions, so if you're using a real Firebase project instead of the emulators, you need to upgrade to the Blaze pricing plan.
Want to skip ahead?
You can download a completed version of the codelab. If you get stuck along the way or if you want to see what a completed extension looks like, check out the codelab-end
branch of the GitHub repository or download the completed zip.
3. Review the code
- Open the
index.ts
file from the zip file. Notice that it contains two Cloud Functions declarations within it.
What do these functions do?
These demo functions are used for geohashing. They take a coordinate pair and turn them into a format that is optimized for geo queries in Firestore. The functions simulate the use of an API call so that you can learn more about handling sensitive data types in extensions. For more information, see the documentation on running Geo queries on data in Firestore.
Function constants
Constants are declared early on, at the top of the index.ts
file. Some of these constants are referenced in the extension's defined triggers.
index.ts
import {firestore} from "firebase-functions";
import {initializeApp} from "firebase-admin/app";
import {GeoHashService, ResultStatusCode} from "./fake-geohash-service";
import {onCall} from "firebase-functions/v1/https";
import {fieldValueExists} from "./utils";
const documentPath = "users/{uid}";
const xField = "xv";
const yField = "yv";
const apiKey = "1234567890";
const outputField = "hash";
initializeApp();
const service = new GeoHashService(apiKey);
Firestore Trigger
The first function in the index.ts
file looks like this:
index.ts
export const locationUpdate = firestore.document(documentPath)
.onWrite((change) => {
// item deleted
if (change.after == null) {
return 0;
}
// double check that both values exist for computation
if (
!fieldValueExists(change.after.data(), xField) ||
!fieldValueExists(change.after.data(), yField)
) {
return 0;
}
const x: number = change.after.data()![xField];
const y: number = change.after.data()![yField];
const hash = service.convertToHash(x, y);
// This is to check whether the hash value has changed. If
// it hasn't, you don't want to write to the document again as it
// would create a recursive write loop.
if (fieldValueExists(change.after.data(), outputField)
&& change.after.data()![outputField] == hash) {
return 0;
}
return change.after.ref
.update(
{
[outputField]: hash.hash,
}
);
});
This function is a Firestore trigger. When a write event occurs in the database, the function reacts to that event by searching for an xv
field and a yv
field, and, if both of those fields exist, it calculates the geohash and writes the output to a specified document output location. The input document is defined by the users/{uid}
constant, which means that the function reads every document written to the users/
collection and then processes a geohash for those documents. It then outputs the hash to a hash field in the same document.
Callable Functions
The next function in the index.ts
file looks like this:
index.ts
export const callableHash = onCall((data, context) => {
if (context.auth == undefined) {
return {error: "Only authorized users are allowed to call this endpoint"};
}
const x = data[xField];
const y = data[yField];
if (x == undefined || y == undefined) {
return {error: "Either x or y parameter was not declared"};
}
const result = service.convertToHash(x, y);
if (result.status != ResultStatusCode.ok) {
return {error: `Something went wrong ${result.message}`};
}
return {result: result.hash};
});
Notice the onCall
function. It indicates that this function is a callable function, which can be called from within your client application code. This callable function takes x
and y
parameters and returns a geohash. Although this function won't be called directly in this codelab, it's included here as an example of something to configure in the Firebase extension.
4. Set up an extension.yaml file
Now that you know what the Cloud Functions code in your extension does, you're ready to package it up for distribution. Every Firebase Extension comes with an extension.yaml
file that describes what the extension does and how it behaves.
An extension.yaml
file requires some initial metadata about your extension. Each of the following steps helps you understand what all the fields mean and why you need them.
- Create an
extension.yaml
file in the root directory of the project that you downloaded earlier. Start by adding the following:
name: geohash-ext
version: 0.0.1
specVersion: v1beta # Firebase Extensions specification version (do not edit)
The name of the extension is used as the base of the extension's instance ID (users can install multiple instances of an extension, each with its own ID). Firebase then generates the name of the extension's service accounts and extension-specific resources using that instance ID. The version number indicates the version of your extension. It must follow semantic versioning, and you need to update it whenever you make changes to the functionality of the extension. The extension specification version is used to determine which Firebase extensions specification to follow, in this case, v1beta
is used.
- Add some user-friendly details to the YAML file:
...
displayName: Latitude and longitude to GeoHash converter
description: A converter for changing your Latitude and longitude coordinates to geohashes.
The display name is a friendly representation of your extension's name when developers interact with your extension. The description gives a brief overview of what the extension does. When the extension is deployed on extensions.dev, it looks something like this:
- Specify the license for the code in your extension.
...
license: Apache-2.0 # The license you want for the extension
- Indicate who wrote the extension and whether or not billing is required to install it:
...
author:
authorName: AUTHOR_NAME
url: https://github.com/Firebase
billingRequired: true
The author
section is used to let your users know who to reach out to in the event that they are having issues with the extension or want more information about it. billingRequired
is a required parameter and must be set to true
since all extensions rely on Cloud Functions, which requires the Blaze plan.
This covers the minimum number of fields required in the extension.yaml
file to identify this extension. For more details on other identifying information you can specify in an extension, see the documentation.
5. Convert the Cloud Functions code into an Extensions resource
An extension resource is an item that Firebase creates in the project during an extension's installation. The extension then owns those resources and has a specific service account that operates on them. In this project, those resources are Cloud Functions, which must be defined in the extension.yaml
file because the extension will not automatically create resources from code in the functions folder. If your Cloud Functions aren't explicitly declared as a resource, they cannot be deployed when the extension is deployed.
User-defined deployment location
- Allow the user to specify the location where they want to deploy this extension and decide whether it would be better to host the extension closer to their end users or closer to their database. In the
extension.yaml
file, include the option to pick a location.
extension.yaml
You are now ready to write the configuration for the function resource.
- In the
extension.yaml
file, create a resource object for thelocationUpdate
function. Append the following to theextension.yaml
file:
resources:
- name: locationUpdate
type: firebaseextensions.v1beta.function
properties:
eventTrigger:
eventType: providers/cloud.firestore/eventTypes/document.write
resource: projects/${PROJECT_ID}/databases/(default)/documents/users/{uid}
You define the name
as the function name defined in the index.ts
file of the project. You specify the type
of function that is being deployed, which should always be firebaseextensions.v1beta.function
, for now. Then, you define the properties
of this function. the first property you define is the eventTrigger
that is associated with this function. To mirror what the extension currently supports, you use the eventType
of providers/cloud.firestore/eventTypes/document.write
, which is found in the Write Cloud Functions for your extension documentation. You define the resource
as the location of the documents. Since your current goal is to mirror what exists in the code, the document path listens to users/{uid}
, with the default database location preceding it.
- The extension needs read and write permissions for the Firestore database. At the very end of the
extension.yaml
file, specify the IAM roles that the extension should have access to in order to work with the database in the developer's Firebase project.
roles:
- role: datastore.user
reason: Allows the extension to read / write to your Firestore instance.
The datastore.user
role comes from the list of supported IAM roles for extensions. Since the extension is going to be reading and writing, the datastore.user
role is a good fit here.
- The callable function must be added as well. In the
extension.yaml
file, create a new resource under the resources property. These properties are specific for a callable function:
- name: callableHash
type: firebaseextensions.v1beta.function
properties:
httpsTrigger: {}
Although the previous resource used an eventTrigger
, here you use an httpsTrigger
, which covers both callable functions and HTTPS functions.
Code check
That was a lot of configuration to make your extension.yaml
match everything done by the code in your index.ts
file. This is what the completed extension.yaml
file should look like at this time:
extension.yaml
name: geohash-ext
version: 0.0.1
specVersion: v1beta # Firebase Extensions specification version (do not edit)
displayName: Latitude and Longitude to GeoHash converter
description: A converter for changing your Latitude and Longitude coordinates to geohashes.
license: Apache-2.0 # The license you want for the extension
author:
authorName: Sparky
url: https://github.com/Firebase
billingRequired: true
resources:
- name: locationUpdate
type: firebaseextensions.v1beta.function
properties:
eventTrigger:
eventType: providers/cloud.firestore/eventTypes/document.write
resource: projects/${PROJECT_ID}/databases/(default)/documents/users/{uid}
- name: callableHash
type: firebaseextensions.v1beta.function
properties:
httpsTrigger: {}
roles:
- role: datastore.user
reason: Allows the extension to read / write to your Firestore instance.
Status check
At this point, you have the initial functional pieces of the extension set up, so you can actually try it out using the Firebase emulators!
- If you haven't already, call
npm run build
in the functions folder of the downloaded extensions project. - Create a new directory on your host system and connect that directory to your Firebase project using
firebase init
.
cd .. mkdir sample-proj cd sample-proj firebase init --project=projectID-or-alias
This command creates a `firebase.json` file in the directory. In the following steps, you push the configuration specified in this file to Firebase.
- From the same directory, run
firebase ext:install
. Replace/path/to/extension
with the absolute path to the directory that contains yourextension.yaml
file.
firebase ext:install /path/to/extension
This command does two things:
- It prompts you to specify the configuration for the extension instance, and it creates an
*.env
file that contains the configuration information for the instance. - It adds the extension instance to the
extensions
section of yourfirebase.json
. This acts as a map of instance ID to extension version. - Since you are deploying the project locally, you can specify that you'd like to use a local file rather than the Google Cloud Secret Manager.
- Start the Firebase emulators with the new configuration:
firebase emulators:start
- After running
emulators:start
, navigate to the Firestore tab in the webview of the emulators. - Add a document into the
users
collection with anxv
number field andyv
number field.
- If you were successful in installing the extension, the extension creates a new field called
hash
in the document.
Clean up to avoid conflicts
- Once you are done testing, uninstall the extension—you are going to update the extension code and don't want to conflict with the current extension later on.
Extensions allow multiple versions of the same extension to be installed at once, so by uninstalling, you ensure that there are no conflicts with a previously installed extension.
firebase ext:uninstall geohash-ext
The current solution works, but as mentioned in the beginning of the project, there is a hard-coded API key to simulate communicating with a service. How can you use the end-user's API key instead of the one originally supplied? Read on to find out.
6. Make the extension user configurable
At this point in the codelab, you have an extension that is configured for use with the opinionated setup of the functions that you have already written, but what if your user wants to use latitude and longitude instead of y and x for the fields indicating the location on a cartesian plane? Also, how can you get the end user to supply their own API key, rather than let them consume the API key supplied? You could quickly overrun the quota for that API. In this case, you set up and use parameters.
Define basic parameters in the extension.yaml
file
Start by converting the items that developers may potentially have a custom configuration for. The first would be the XFIELD
and YFIELD
parameters.
- In the
extension.yaml
file, add the following code, which uses theXFIELD
and theYFIELD
field parameters. These parameters live inside the previously definedparams
YAML property:
extension.yaml
params:
- param: XFIELD
label: The X Field Name
description: >-
The X Field is also known as the **longitude** value. What does
your Firestore instance refer to as the X value or the longitude
value. If no value is specified, the extension searches for
field 'xv'.
type: string
validationRegex: ^\D([0-9a-zA-Z_.]{0,375})$
validationErrorMessage: >-
The field can only contain uppercase or lowercase letter, numbers,
_, and . characters and must be less than 1500 bytes long. The field
must also not start with a number.
default: xv
required: false
immutable: false
example: xv
- param: YFIELD
label: The Y Field Name
description: >-
The Y Field is also known as the **latitude** value. What does
your Firestore instance refer to as the Y value or the latitude
value. If no value is specified, the extension searches for
field 'yv'.
type: string
validationRegex: ^\D([0-9a-zA-Z_.]{0,375})$
validationErrorMessage: >-
The field can only contain uppercase or lowercase letter, numbers,
_, and . characters and must be less than 1500 bytes long. The field
must also not start with a number.
default: yv
required: false
immutable: false
example: yv
- param names the parameter in a way that is visible to you, the extension producer. Use this value later on when specifying the parameter values.
- label is a human-readable identifier to the developer to let them know what the parameter does.
- description gives a detailed description of the value. Since this supports markdown, it can link to extra documentation, or it can highlight words that might be important to the developer.
- type defines the input mechanism for how a user would set the parameter value. There are many types that exist, including
string
,select
,multiSelect
,selectResource
, andsecret
. To learn more about each of these options, see the documentation. - validationRegex constraints the developer entry to a certain regex value (in the example it's based on the simple field name guidelines found here); and if that fails...
- validationErrorMessage alerts the developer to the failure value.
- default is what the value would be if the developer did not input any text.
- required means that the developer is not required to enter any text.
- immutable allows the developer to update this extension and change this value. In this case, the developer should be able to change field names as their requirements change.
- example provides an idea of what a valid input may look like.
That was a lot to understand!
- You have three more parameters to add to the
extension.yaml
file before adding a special parameter.
- param: INPUTPATH
label: The input document to listen to for changes
description: >-
This is the document where you write an x and y value to. Once
that document has received a value, it notifies the extension to
calculate a geohash and store that in an output document in a certain
field. This accepts function [wildcard parameters](https://firebase.google.com/docs/functions/firestore-events#wildcards-parameters)
type: string
validationRegex: ^[^/]+(/[^/]*/[^/]*)*/[^/]+$
validationErrorMessage: >-
This must point to a document path, not a collection path from the root
of the database. It must also not start or end with a '/' character.
required: true
immutable: false
example: users/{uid}
- param: OUTPUTFIELD
label: Geohash field
description: >-
This specifies the field in the output document to store the geohash in.
type: string
validationRegex: ^\D([0-9a-zA-Z_.]{0,375})$
validationErrorMessage: >-
The field can only contain uppercase or lowercase letter, numbers,
_, and . characters and must be less than 1500 bytes long. The field
must also not start with a number.
required: false
default: hash
immutable: false
example: hash
Define sensitive parameters
Now, you need to manage the API key that the user specifies. This is a sensitive string that should not be stored in plain text in the function. Instead, store this value in the Cloud secret manager. This is a special location in the cloud that stores encrypted secrets, and prevents them from being accidentally leaked. This requires the developer to pay for the use of this service, but it adds an extra layer of security over their API keys and potentially limits fraudulent activity. The user documentation alerts the developer that it is a paid service, so that there aren't any surprises in billing. Overall, the use is similar to the other string resources mentioned above. The only difference is the type which is called secret
.
- In the
extension.yaml
file, add the following code:
extension.yaml
- param: APIKEY
label: GeohashService API Key
description: >-
Your geohash service API Key. Since this is a demo, and not a real
service, you can use : 1234567890.
type: secret
required: true
immutable: false
Update the resource
attributes to use parameters
As mentioned previously, the resource (not the function) defines how the resource is observed, so the locationUpdate
resource needs to be updated in order to use the new parameter.
- In the
extension.yaml
file, add the following code:
extension.yaml
## Change from this
- name: locationUpdate
type: firebaseextensions.v1beta.function
properties:
eventTrigger:
eventType: providers/cloud.firestore/eventTypes/document.write
resource: projects/${PROJECT_ID}/databases/(default)/documents/users/{uid}]
## To this
- name: locationUpdate
type: firebaseextensions.v1beta.function
properties:
eventTrigger:
eventType: providers/cloud.firestore/eventTypes/document.write
resource: projects/${PROJECT_ID}/databases/(default)/documents/${INPUTPATH}
Check the extension.yaml
file
- Review the
extension.yaml
file. It should look something like this:
extension.yaml
name: geohash-ext
version: 0.0.1
specVersion: v1beta # Firebase Extensions specification version (do not edit)
displayName: Latitude and Longitude to GeoHash converter
description: A converter for changing your Latitude and Longitude coordinates to geohashes.
license: Apache-2.0 # The license you want to use for the extension
author:
authorName: Sparky
url: https://github.com/Firebase
billingRequired: true
params:
- param: XFIELD
label: The X Field Name
description: >-
The X Field is also known as the **longitude** value. What does
your Firestore instance refer to as the X value or the longitude
value. If you don't provide a value for this field, the extension will use 'xv' as the default value.
type: string
validationRegex: ^\D([0-9a-zA-Z_.]{0,375})$
validationErrorMessage: >-
The field can only contain uppercase or lowercase letter, numbers,
_, and . characters and must be less than 1500 bytes long. The field
must also not start with a number.
default: xv
required: false
immutable: false
example: xv
- param: YFIELD
label: The Y Field Name
description: >-
The Y Field is also known as the **latitude** value. What does
your Firestore instance refer to as the Y value or the latitude
Value. If you don't provide a value for this field, the extension will use 'yv' as the default value.
type: string
validationRegex: ^\D([0-9a-zA-Z_.]{0,375})$
validationErrorMessage: >-
The field can only contain uppercase or lowercase letter, numbers,
_, and . characters and must be less than 1500 bytes long. The field
must also not start with a number.
default: yv
required: false
immutable: false
example: yv
- param: INPUTPATH
label: The input document to listen to for changes
description: >-
This is the document where you write an x and y value to. Once
that document has been modified, it notifies the extension to
compute a geohash and store that in an output document in a certain
field. This accepts function [wildcard parameters](https://firebase.google.com/docs/functions/firestore-events#wildcards-parameters)
type: string
validationRegex: ^[^/]+(/[^/]*/[^/]*)*/[^/]+$
validationErrorMessage: >-
This must point to a document path, not a collection path from the root
of the database. It must also not start or end with a '/' character.
required: true
immutable: false
example: users/{uid}
- param: OUTPUTFIELD
label: Geohash field
description: >-
This specifies the field in the output document to store the geohash in.
type: string
validationRegex: ^\D([0-9a-zA-Z_.]{0,375})$
validationErrorMessage: >-
The field can only contain uppercase or lowercase letter, numbers,
_, and . characters and must be less than 1500 bytes long. The field
must also not start with a number.
required: false
default: hash
immutable: false
example: hash
- param: APIKEY
label: GeohashService API Key
description: >-
Your geohash service API Key. Since this is a demo, and not a real
service, you can use : 1234567890.
type: secret
required: true
immutable: false
resources:
- name: locationUpdate
type: firebaseextensions.v1beta.function
properties:
eventTrigger:
eventType: providers/cloud.firestore/eventTypes/document.write
resource: projects/${PROJECT_ID}/databases/(default)/documents/${INPUTPATH}
- name: callableHash
type: firebaseextensions.v1beta.function
properties:
httpsTrigger: {}
roles:
- role: datastore.user
reason: Allows the extension to read / write to your Firestore instance.
Access parameters in code
Now that all of the parameters are configured in the extension.yaml
file, add them to the index.ts
file.
- In the
index.ts
file, replace the default values withprocess.env.PARAMETER_NAME
, which fetches the appropriate parameter values and populates them in the function code deployed on the developer's Firebase project.
index.ts
// Replace this:
const documentPath = "users/{uid}";
const xField = "xv";
const yField = "yv";
const apiKey = "1234567890";
const outputField = "hash";
// with this:
const documentPath = process.env.INPUTPATH!; // this value is ignored since its read from the resource
const xField = process.env.XFIELD!;
const yField = process.env.YFIELD!;
const apiKey = process.env.APIKEY!;
const outputField = process.env.OUTPUTFIELD!;
Normally, you want to perform null checks with the environment variable values, but in this case, you trust that the parameter values are correctly copied. The code is now configured to work with the extension parameters.
7. Create user documentation
Before testing the code on emulators or in the Firebase extensions marketplace, the extension needs to be documented so that developers know what they are getting when they use the extension.
- Start by creating the
PREINSTALL.md
file, which is used to describe the functionality, any prerequisites for installation, and potential billing implications.
PREINSTALL.md
Use this extension to automatically convert documents with a latitude and
longitude to a geohash in your database. Additionally, this extension includes a callable function that allows users to make one-time calls
to convert an x,y coordinate into a geohash.
Geohashing is supported for latitudes between 90 and -90 and longitudes
between 180 and -180.
#### Third Party API Key
This extension uses a fictitious third-party API for calculating the
geohash. You need to supply your own API keys. (Since it's fictitious,
you can use 1234567890 as an API key).
#### Additional setup
Before installing this extension, make sure that you've [set up a Cloud
Firestore database](https://firebase.google.com/docs/firestore/quickstart) in your Firebase project.
After installing this extension, you'll need to:
- Update your client code to point to the callable geohash function if you
want to perform arbitrary geohashes.
Detailed information for these post-installation tasks are provided after
you install this extension.
#### Billing
To install an extension, your project must be on the [Blaze (pay as you
go) plan](https://firebase.google.com/pricing)
- This extension uses other Firebase and Google Cloud Platform services,
which have associated charges if you exceed the service's no-cost tier:
- Cloud Firestore
- Cloud Functions (Node.js 16+ runtime. [See
FAQs](https://firebase.google.com/support/faq#extensions-pricing))
- [Cloud Secret Manager](https://cloud.google.com/secret-manager/pricing)
- To save time on writing the
README.md
for this project, use the convenience method:
firebase ext:info . --markdown > README.md
This combines the contents of your PREINSTALL.md
file and additional details about your extension from your extension.yaml
file.
Finally, inform the developer of the extension about some additional details regarding the extension that was just installed. The developer may get some additional instructions and information after completing the installation and may get some detailed post-installation tasks like setting up client code here.
- Create a
POSTINSTALL.md
file, and then include the following post installation information:
POSTINSTALL.md
Congratulations on installing the geohash extension!
#### Function information
* **Firestore Trigger** - ${function:locationUpdate.name} was installed
and is invoked when both an x field (${param:XFIELD}) and y field
(${param:YFIELD}) contain a value.
* **Callable Trigger** - ${function:callableHash.name} was installed and
can be invoked by writing the following client code:
```javascript
import { getFunctions, httpsCallable } from "firebase/functions";
const functions = getFunctions();
const geoHash = httpsCallable(functions, '${function:callableHash.name}');
geoHash({ ${param:XFIELD}: -122.0840, ${param:YFIELD}: 37.4221 })
.then((result) => {
// Read result of the Cloud Function.
/** @type {any} */
const data = result.data;
const error = data.error;
if (error != null) {
console.error(`callable error : ${error}`);
}
const result = data.result;
console.log(result);
});
Monitoring
As a best practice, you can monitor the activity of your installed extension, including checks on its health, usage, and logs.
The output rendering looks something like this when it's deployed:
<img src="img/82b54a5c6ca34b3c.png" alt="A preview of the latitude and longitude geohash converter extension in the firebase console" width="957.00" />
## Test the extension with the full configuration
Duration: 03:00
It's time to make sure that the user-configurable extension is working the way it is intended.
* Change into the functions folder and ensure that the latest compiled version of the extensions exists. In the extensions project functions directory, call:
```console
npm run build
This recompiles the functions so the latest source code is ready for deployment alongside the extension when it's deployed to an emulator or to Firebase directly.
Next, create a new directory to test the extension from. Since the extension was developed from existing functions, do not test from the folder that the extension was configured in as that also attempts to deploy the functions and Firebase rules alongside it.
Install and test with the Firebase emulators
- Create a new directory on your host system and connect that directory to your Firebase project using
firebase init
.
mkdir sample-proj cd sample-proj firebase init --project=projectID-or-alias
- From that directory, run
firebase ext:install
to install the extension. Replace/path/to/extension
with the absolute path to the directory that contains yourextension.yaml
file. This starts the installation process for your extension and creates a.env
file that contains your configurations before pushing the configuration to Firebase or to the emulators.
firebase ext:install /path/to/extension
- Since you are deploying the project locally, specify that you would like to use a local file rather than the Google Cloud Secret Manager.
- Start the local emulator suite:
firebase emulators:start
Install and test with a real Firebase project
You can install your extension in an actual Firebase project. It is recommended to use a test project for your testing. Use this testing workflow if you want to test your extension's end-to-end flow or if your extension's trigger isn't yet supported by the Firebase emulator suite (see the Extensions emulator option). The emulators currently support HTTP request-triggered functions and background event-triggered functions for Cloud Firestore, Realtime Database, and Pub/Sub.
- Create a new directory on your host system and connect that directory to your Firebase project using
firebase init
.
cd .. mkdir sample-proj cd sample-proj firebase init --project=projectID-or-alias
- Then, from that directory, run
firebase ext:install
to install the extension. Replace/path/to/extension
with the absolute path to the directory that contains yourextension.yaml
file. This starts the installation process for your extension and creates a.env
file that contains your configurations before pushing the configuration to Firebase or to the emulators.
firebase ext:install /path/to/extension
- Since you want to deploy to Firebase directly, and want to use the Google Cloud Secret Manager, you need to activate the Secret Manager API before installing the extension.
- Deploy to your Firebase project.
firebase deploy
Test out the extension
- After running
firebase deploy
orfirebase emulators:start
, navigate to the Firestore tab of either the Firebase console or the webview of the emulators, as appropriate. - Add a document into the collection specified by the
x
field andy
field. In this case, updated documents are located atu/{uid}
with anx
field ofxv
and ay
field ofyv
.
- If you were successful in installing the extension, the extension creates a new field called
hash
in the document after you save the two fields.
8. Congratulations!
You've successfully converted your first Cloud Function into a Firebase Extension!
You added an extension.yaml
file and configured it so developers can select how they would like your extension to be deployed. You then created user documentation that provides guidance for what the developers of the extension should do prior to setting up the extension and what steps they might need to take after having successfully installed the extension.
You now know the key steps required to convert a Firebase Function into a distributable Firebase Extension.