arrow_back

Integrate Cloud Run Functions with Firestore

Sign in Join
Get access to 700+ labs and courses

Integrate Cloud Run Functions with Firestore

Lab 45 minutes universal_currency_alt 5 Credits show_chart Introductory
info This lab may incorporate AI tools to support your learning.
Get access to 700+ labs and courses

Overview

Cloud Run functions can extend your applications and services by integrating with Google Cloud databases namely Firestore, Cloud Spanner, Cloud SQL, Cloud Bigtable and with Memorystore, Google Cloud's in-memory datastore cache service.

In this lab, you create Cloud Run functions that integrate with Firestore, Google Cloud's serverless NoSQL document database. You'll use the Cloud Run functions Framework and Firestore client library for Node.js to create functions, and set up triggers to execute them when events occur in the database.

A Firestore function's lifecycle typically involves these steps:

  • Wait for changes to a particular document in the Firestore database.
  • Trigger when an event occurs.
  • Perform tasks using a data object that is received with a snapshot of the affected document.

Objectives

In this lab, you will:

  • Set up a Firestore database.
  • Develop and deploy an event-driven function to log information when a document is created in Firestore.
  • Develop and deploy an event-driven function to update the document contents.
  • Access and use Secrets with Cloud Run functions.
  • Use the Google Cloud console to view logs generated by your function.

Setup

Before you click the Start Lab button

Note: Read these instructions.

Labs are timed and you cannot pause them. The timer, which starts when you click Start Lab, shows how long Google Cloud resources will be made available to you.

This Qwiklabs hands-on lab lets you do the lab activities yourself in a real cloud environment, not in a simulation or demo environment. It does so by giving you new, temporary credentials that you use to sign in and access Google Cloud for the duration of the lab.

What you need

To complete this lab, you need:

  • Access to a standard internet browser (Chrome browser recommended).
  • Time to complete the lab.
Note: If you already have your own personal Google Cloud account or project, do not use it for this lab. Note: If you are using a Pixelbook, open an Incognito window to run this lab.

Activate Google Cloud Shell

Google Cloud Shell is a virtual machine that is loaded with development tools. It offers a persistent 5GB home directory and runs on the Google Cloud.

Google Cloud Shell provides command-line access to your Google Cloud resources.

  1. In Cloud console, on the top right toolbar, click the Open Cloud Shell button.

  2. Click Continue.

It takes a few moments to provision and connect to the environment. When you are connected, you are already authenticated, and the project is set to your PROJECT_ID. For example:

gcloud is the command-line tool for Google Cloud. It comes pre-installed on Cloud Shell and supports tab-completion.

  • You can list the active account name with this command:
gcloud auth list

Output:

Credentialed accounts: - @.com (active)

Example output:

Credentialed accounts: - google1623327_student@qwiklabs.net
  • You can list the project ID with this command:
gcloud config list project

Output:

[core] project =

Example output:

[core] project = qwiklabs-gcp-44776a13dea667a6 Note: Full documentation of gcloud is available in the gcloud CLI overview guide .

Task 1. Set up the environment

In this task, you set up environment variables and enable relevant service APIs that are needed to perform this lab.

Set environment variables

Before you create Cloud Run functions, you set some environment variables.

  1. Sign in to the Google Cloud console with your lab credentials, and open the Cloud Shell terminal window.

  2. Run the following command in Cloud Shell to set your Project ID and REGION environment variables.

    PROJECT_ID=$(gcloud config get-value project) REGION={{{project_0.default_region|set at lab start}}}
  3. Set an environment variable for the Project Number:

    PROJECT_NUMBER=$(gcloud projects list \ --filter="project_id:$PROJECT_ID" \ --format='value(project_number)')
  4. Set the default region for Cloud Run functions:

    gcloud config set functions/region {{{project_0.default_region|set at lab start}}}

Enable APIs

  1. To enable service APIs that are needed for this lab, run the following command:

    gcloud services enable \ artifactregistry.googleapis.com \ cloudfunctions.googleapis.com \ cloudbuild.googleapis.com \ eventarc.googleapis.com \ run.googleapis.com \ logging.googleapis.com \ storage.googleapis.com \ pubsub.googleapis.com

Task 2. Set up Firestore

To perform the tasks in this lab, you need to set up a Firestore database. Firestore stores data in the form of documents and collections. To use Cloud Run functions with Firestore, you must first set up Firestore before deploying the functions.

  1. In the Google Cloud Console, click the Search bar in the top navigation and type Firestore. Select Firestore from the search results.

  2. Click Create a Firestore database.

  3. Select Standard Edition.

  4. Under Configuration options, select Firestore Native.

  5. For Security rules, choose Open.

  6. In Location type, click Region, and then select the lab region set at lab start from the list.

    Note: If the list of regions is not populated, refresh the browser or try the wizard again from the Cloud console menu.
  7. Leave the other settings as their defaults, and click Create Database.

Click Check my progress to verify the objective. Set up Firestore.

Task 3. Develop an event-driven function for new Firestore documents

After your Firestore database is created, you can develop your function code. In this task, you write your function's source code that responds to the creation of new documents in the database. The function logs information about the data received in the function invocation.

Set up your working directory

Firestore functions are invoked with a cloudevents data structure that can be decoded using Protocol Buffers with the protobuf.js NPM module. For more information see the links that are provided at the end of the lab.

  1. Copy the required .proto and dependency files into a directory named firestore_functions:

    gcloud storage cp -R gs://cloud-training/CBL493/firestore_functions .
  2. Change to the firestore_functions directory:

    cd firestore_functions

    The firestore_functions directory also contains empty node.js and package.json files which you will update in the next subtask.

Write the function code

  1. In the Cloud Shell toolbar, click Open Editor.

    You can switch between Cloud Shell and the code editor using Open Editor and Open Terminal, or click Open in new window to leave the editor open in a separate tab.
  2. In the editor, add the following code to the firestore-functions/index.js file:

    /** * Cloud Event Function triggered by a change to a Firestore document. */ const functions = require('@google-cloud/functions-framework'); const protobuf = require('protobufjs'); functions.cloudEvent('newCustomer', async cloudEvent => { console.log(`Function triggered by event on: ${cloudEvent.source}`); console.log(`Event type: ${cloudEvent.type}`); console.log('Loading protos...'); const root = await protobuf.load('data.proto'); const DocumentEventData = root.lookupType('google.events.cloud.firestore.v1.DocumentEventData'); console.log('Decoding data...'); const firestoreReceived = DocumentEventData.decode(cloudEvent.data); console.log('\nNew document:'); console.log(JSON.stringify(firestoreReceived.value, null, 2)); const documentData = firestoreReceived.value.fields; console.log('Document data:', documentData); }); The code uses the functions framework Node.js library to create a function that processes data delivered using the cloudEvent specification.
  3. In the editor, add the following to the firestore-functions/package.json file:

    { "name": "firestore_functions", "version": "0.0.1", "main": "index.js", "dependencies": { "@google-cloud/functions-framework": "^3.1.3", "protobufjs": "^7.2.2", "@google-cloud/firestore": "^6.0.0" } }

Update service account permissions

Grant the Cloud Run functions service agent certain permissions before deploying the function. Run the following commands in Cloud Shell.

  1. Click Open Terminal.

  2. Set an environment variable for the Cloud Run functions service agent's service account:

    SERVICE_ACCOUNT=service-$PROJECT_NUMBER@gcf-admin-robot.iam.gserviceaccount.com
  3. To view and get artifacts from Artifact Registry, grant the artifactregistry.reader role to the Cloud Run functions service account:

    gcloud projects add-iam-policy-binding $PROJECT_ID --member serviceAccount:$SERVICE_ACCOUNT --role roles/artifactregistry.reader Note: If you receive an error from the previous step indicating that the service account does not exist, or that the request has invalid authentication credentials, perform the following steps:
  4. Disable the Cloud Functions API:

    gcloud services disable cloudfunctions.googleapis.com
  5. Re-enable the Cloud Functions API:

    gcloud services enable cloudfunctions.googleapis.com
  6. Wait a few seconds, and then rerun the command to grant the artifactregistry.reader role to the Cloud Run functions service account:

    gcloud projects add-iam-policy-binding $PROJECT_ID --member serviceAccount:$SERVICE_ACCOUNT --role roles/artifactregistry.reader

Deploy the function

  • To deploy the function, run the following command from Cloud Shell:

    gcloud functions deploy newCustomer \ --gen2 \ --runtime=nodejs20 \ --region={{{project_0.default_region|set at lab start}}} \ --trigger-location={{{project_0.default_region|set at lab start}}} \ --source=. \ --entry-point=newCustomer \ --trigger-event-filters=type=google.cloud.firestore.document.v1.created \ --trigger-event-filters=database='(default)' \ --trigger-event-filters-path-pattern=document='customers/{name}' Firestore supports the created, updated, deleted, and written events which you specify as the trigger-event-filter option to the deploy command. The path pattern states that all documents in the customers collection should be monitored.

    After the command executes successfully, the command generates the URL for the function endpoint, as shown in this sample partial command output:

    ... state: ACTIVE updateTime: '2024-03-19T21:38:04.917134057Z' url: https://{{{project_0.default_region|set at lab start}}}-{{{project_0.default_region}}}.cloudfunctions.net/newCustomer If you receive an error when deploying the function, wait a few minutes before retrying the deploy command. The service account permissions for Eventarc might take some time to propagate.

Test the function

  1. Navigate to Firestore Studio in the Cloud console.

  2. To create a new document collection, click Start collection.

  3. For Collection ID, type customers

  4. To generate an ID for a document in this collection, click into Document ID.

  5. For this document, add a field with the following values:

    Field name Field type Field value
    firstname string Lucas
  6. Click Save.

  7. To verify that your Cloud Run function was invoked, on the Navigation menu (), click Cloud Run.

  8. Click the function name newCustomer.

  9. Click Logs.

  10. Verify that the log entries generated from the function code are present and display the data from the database document that you created.

    You might need to click Refresh to view the latest log entries.

Click Check my progress to verify the objective. Develop an event-driven function for new Firestore documents.

Task 4. Develop an event-driven function for Firestore to update a document

In this task, you develop a function that is triggered when a document is updated in the Firestore database. Your function adds a new field to the document with a value that is derived from the values of some of the other document's fields.

Write the function code

  1. In the editor, add the following code below in the firestore-functions/index.js file:

    const Firestore = require('@google-cloud/firestore'); const firestore = new Firestore({ projectId: process.env.GOOGLE_CLOUD_PROJECT, }); functions.cloudEvent('updateCustomer', async cloudEvent => { console.log('Loading protos...'); const root = await protobuf.load('data.proto'); const DocumentEventData = root.lookupType( 'google.events.cloud.firestore.v1.DocumentEventData' ); console.log('Decoding data...'); const firestoreReceived = DocumentEventData.decode(cloudEvent.data); const resource = firestoreReceived.value.name; const affectedDoc = firestore.doc(resource.split('/documents/')[1]); // Fullname already exists, so don't update again to avoid infinite loop. if (firestoreReceived.value.fields.hasOwnProperty('fullname')) { console.log('Fullname is already present in document.'); return; } if (firestoreReceived.value.fields.hasOwnProperty('lastname')) { const lname = firestoreReceived.value.fields.lastname.stringValue; const fname = firestoreReceived.value.fields.firstname.stringValue; const fullname = `${fname} ${lname}` console.log(`Adding fullname --> ${fullname}`); await affectedDoc.update({ fullname: fullname }); } }); You can define multiple functions with their own entry points in the same project main file and deploy them separately to Cloud Run functions.

    With this approach, every function may share the same set of dependencies even if some of those functions do not need those dependencies.

    To minimize the number of dependencies needed for a particular function and reduce it's memory requirements, it is recommended to keep each function's source code in it's own top-level directory with it's own project configuration files.

Deploy the function

  1. To deploy the new function, run the following command from Cloud Shell:

    gcloud functions deploy updateCustomer \ --gen2 \ --runtime=nodejs20 \ --region={{{project_0.default_region|set at lab start}}} \ --trigger-location={{{project_0.default_region|set at lab start}}} \ --source=. \ --entry-point=updateCustomer \ --trigger-event-filters=type=google.cloud.firestore.document.v1.updated \ --trigger-event-filters=database='(default)' \ --trigger-event-filters-path-pattern=document='customers/{name}'
  2. Verify the command output indicating that the function has been deployed and the state is Active.

Test the function

  1. In the Cloud Console, in Firestore Studio, select the existing documents in the customers collection with a firstname field value of Lucas.

  2. For this document, click Add Field.

  3. Add a field with the following values:

    Field name Field type Field value
    lastname string Sherman
  4. Click Save Field.

  5. Wait for a few seconds, and then verify that you see a new field fullname is added to the document.

    This indicates that your function updateCustomer was invoked when the document was updated.

  6. To verify that your Cloud Run function was invoked, on the Navigation menu (), click Cloud Run.

  7. Click the function name updateCustomer.

  8. Click Logs.

  9. Verify that the log entries generated from the function code are present that indicate that the fullname field was added to the document.

    You might need to click Refresh to view the latest log entries.

Click Check my progress to verify the objective. Develop an event-driven function for Firestore to update a document.

Task 5. Use Secrets with Cloud Run functions

Secret Manager is a Google Cloud service that securely stores data like API keys, passwords, certificates, credentials, and other sensitive information. You can then access these secrets from Cloud Run functions or other services for use in your function logic or service implementation.

In this task, you create and store a credential as a secret in Secret Manager. You develop a function to access the key in your function logic.

Create a secret

  1. To create and use secrets, run the following command in Cloud Shell and enable the Secret Manager API:

    gcloud services enable secretmanager.googleapis.com
  2. Create and store a secret named api-cred with value secret_api_key in Secret Manager:

    echo -n "secret_api_key" | gcloud secrets create api-cred --replication-policy="automatic" --data-file=- The replication-policy is used to determine which regions are used to store secrets and their versions. To specify one or more regions where a secret can be replicated, you can set the replication-policy to user-managed.

Grant access

To access a secret, your function's runtime service account must be granted access to the secret.

  • By default, Cloud Run functions uses the Compute Engine default service account as a function's runtime service account.

    To authenticate with Secret Manager, grant the Secret Manager Secret Accessor role to the Compute Engine default service account:

    gcloud secrets add-iam-policy-binding api-cred --member=serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com --project=$PROJECT_ID --role='roles/secretmanager.secretAccessor' Note: For production use, you should configure your function to authenticate with its own user-managed service account, that is assigned the least-permissive set of roles required to accomplish that function's tasks.

Access the secret from your function

In this subtask, you modify the previously developed newCustomer function to access the secret.

  • In the editor, add the following code to the newCustomer function in the index.js file. Add the code at the end of the function after the last console.log statement in the function body:

    // BEGIN access a secret const fs = require('fs/promises'); try { const secret = await fs.readFile('/etc/secrets/api_cred/latest', { encoding: 'utf8' }); // use the secret. For lab testing purposes, we log the secret. console.log('secret: ', secret); } catch (err) { console.log(err); } // End access a secret The function code reads the secret value from a file that is mounted to a volume that is accessible to the function. You configure this method of accessing the secret when you deploy the function in the next step.

Redeploy the function

  1. In Cloud Shell, redeploy the newCustomer function with the secret:

    gcloud functions deploy newCustomer \ --gen2 \ --runtime=nodejs20 \ --region={{{project_0.default_region|set at lab start}}} \ --trigger-location={{{project_0.default_region|set at lab start}}} \ --source=. \ --entry-point=newCustomer \ --trigger-event-filters=type=google.cloud.firestore.document.v1.created \ --trigger-event-filters=database='(default)' \ --trigger-event-filters-path-pattern=document='customers/{name}' \ --set-secrets '/etc/secrets/api_cred/latest=api-cred:latest' The set-secrets deploy command option specifies the mount path: /etc/secrets/api_cred, and the secret path: /latest.

    By referencing a secret as a volume, your function accesses the latest secret value from Secret Manager each time the file is read from disk.

    Your function can also access a specific version of the secret value as an environment variable. View the documentation on using secrets with Cloud Run functions for more information.
  2. After the function is deployed, verify that it has access to the secret:

    gcloud functions describe newCustomer

    The output from the describe command includes information about the secret. Here's a partial output from the command:

    ... secretVolumes: - mountPath: /etc/secrets/api_cred projectId: '{{{project_0.project_id|Project_ID}}}' secret: api-cred versions: - path: /latest version: latest ...

Test the function

  1. To test the function, repeat the test from the previous task to add a new customer document from Firestore Studio in Cloud console.

  2. To view the function's logs in the Cloud console, on the Navigation menu (), click Cloud Run.

  3. Click the newCustomer function name.

  4. To view the function's logs, click Logs.

    Verify that the entry to log the value of the secret key is present:

    secret: secret_api_key Note: While it is not a general practice to log the value of secrets, this step is used to confirm that the function has access to its value, and can then use the secret in its code.

Click Check my progress to verify the objective. Use Secrets with Cloud Run functions.

Congratulations!

In this lab, you set up a Firestore database, and developed an event-driven cloud function that is triggered when a new document is created in the database. You also developed a function to add a new field to a document when that document is updated. You also created and accessed a secret from a Cloud Run function and used logs to verify the secret value.

Next Steps / Learn More

To learn more about Cloud Run functions for Firestore and other topics, view the documentation:

Copyright 2022 Google LLC All rights reserved. Google and the Google logo are trademarks of Google LLC. All other company and product names may be trademarks of the respective companies with which they are associated.

Before you begin

  1. Labs create a Google Cloud project and resources for a fixed time
  2. Labs have a time limit and no pause feature. If you end the lab, you'll have to restart from the beginning.
  3. On the top left of your screen, click Start lab to begin

This content is not currently available

We will notify you via email when it becomes available

Great!

We will contact you via email if it becomes available

One lab at a time

Confirm to end all existing labs and start this one

Use private browsing to run the lab

Use an Incognito or private browser window to run this lab. This prevents any conflicts between your personal account and the Student account, which may cause extra charges incurred to your personal account.