Send notifications for a web app using Cloud Messaging and Cloud Functions

1. Overview

In this codelab, you'll learn how to use Cloud Functions for Firebase to add functionality to a chat web app by sending notifications to users of the chat app.

3b1284f5144b54f6.png

What you'll learn

  • Create Google Cloud Functions using the Firebase SDK.
  • Trigger Cloud Functions based on Auth, Cloud Storage, and Cloud Firestore events.
  • Add Firebase Cloud Messaging support to your web app.

What you'll need

  • A credit card. Cloud Functions for Firebase requires the Firebase Blaze plan, which means you will have to enable billing on your Firebase project using a credit card.
  • The IDE/text editor of your choice such as WebStorm , Atom or Sublime .
  • A terminal to run shell commands with NodeJS v9 installed.
  • A browser such as Chrome.
  • The sample code. See next step for this.

2. Get the sample code

Clone the GitHub repository from the command line:

 git clone https://github.com/firebase/friendlychat 

Import the starter app

Using your IDE, open or import theandroid_studio_folder.png cloud-functions-start directory from the sample code directory. This directory contains the starting code for the codelab which consists of a fully functional Chat Web App.

3. Create a Firebase project and Set up your app

Create project

  1. Sign into the Firebase console using your Google Account.
  2. Click the button to create a new project, and then enter a project name (for example, FriendlyChat ).
  3. Click Continue.
  4. If prompted, review and accept the Firebase terms , and then click Continue.
  5. (Optional) Enable AI assistance in the Firebase console (called "Gemini in Firebase").
  6. For this codelab, you do not need Google Analytics, so toggle off the Google Analytics option.
  7. Click Create project, wait for your project to provision, and then click Continue.

Upgrade to the Blaze plan

To use Cloud Functions for Firebase and Cloud Storage for Firebase, your Firebase project needs to be on the pay-as-you go (Blaze) pricing plan , which means it's linked to a Cloud Billing account .

  • A Cloud Billing account requires a payment method, like a credit card.
  • If you're new to Firebase and Google Cloud, check if you're eligible for a $300 credit and a Free Trial Cloud Billing account .
  • If you're doing this codelab as part of an event, ask your organizer if there are any Cloud credits available.

If you do not have access to a credit card or are uncomfortable continuing with the Blaze pricing plan, consider using the Firebase Emulator Suite which will allow you to emulate Cloud Functions for free on your local machine.

All Firebase projects, including those on the Blaze pricing plan, still have access to no-cost usage quotas for Cloud Functions. The steps outlined in this codelab will fall within the free tier usage limits. However, you will see small charges ( about $0.03 ) from Cloud Storage which is used to host your Cloud Functions build images.

To upgrade your project to the Blaze plan, follow these steps:

  1. In the Firebase console, select to upgrade your plan .
  2. Select the Blaze plan. Follow the on-screen instructions to link a Cloud Billing account to your project.
    If you needed to create a Cloud Billing account as part of this upgrade, you might need to navigate back to the upgrade flow in the Firebase console to complete the upgrade.

Enable Google Auth

To let users sign-in the app, we'll use Google auth which needs to be enabled.

In Firebase Console, open the Buildsection > Authentication> Sign-in methodtab (or click here to go there). Then, enable the GoogleSign-in Provider and click Save. This will allow users to sign in on the web app with their Google accounts.

Also, feel free to set the public facing name of your app to Friendly Chat:

8290061806aacb46.png

Set up Cloud Storage for Firebase

The app uses Cloud Storage to upload pictures.

Here's how to set up Cloud Storage for Firebase in your Firebase project:

  1. In the left-panel of the Firebase console, expand Buildand then select Storage .
  2. Click Get started.
  3. Select a location for your default Storage bucket.
    Buckets in US-WEST1 , US-CENTRAL1 , and US-EAST1 can take advantage of the "Always Free" tier for Google Cloud Storage. Buckets in all other locations follow Google Cloud Storage pricing and usage .
  4. Click Start in test mode. Read the disclaimer about the security rules.
    Do not distribute or expose an app publicly without adding Security Rules for your Storage bucket.
  5. Click Create.

Add a Web App

On Firebase Console, add a web app. To do so, go to Project Settings and scroll down to Add app. Pick web as the platform and check the box for setting up Firebase Hosting, then register the app and click Nextfor the remainder of the steps, lastly clicking on Continue to console.

4. Install the Firebase Command Line Interface

The Firebase Command Line Interface (CLI) will allow you to serve the web app locally and deploy your web app and Cloud Functions.

To install or upgrade the CLI run the following npm command:

  npm 
  
 - 
 g 
  
 install 
  
 firebase 
 - 
 tools 
 

To verify that the CLI has been installed correctly, open a console and run:

 firebase --version 

Make sure the version of the Firebase CLI is above 4.0.0so that it has all the latest features required for Cloud Functions. If not, run npm install -g firebase-tools to upgrade as shown above.

Authorize the Firebase CLI by running:

 firebase login 

Make sure you are in the cloud-functions-start directory, then set up the Firebase CLI to use your Firebase Project:

 firebase use --add 

Next, select your Project ID and follow the instructions. When prompted, you can choose any Alias, such as codelab .

5. Deploy and run the web app

Now that you have imported and configured your project, you are ready to run the web app for the first time! Open a terminal window, navigate to the cloud-functions-start folder, and deploy the web app to Firebase hosting using:

 firebase deploy --except functions 

This is the console output you should see:

  i 
  
 deploying 
  
 database 
 , 
  
 storage 
 , 
  
 hosting 
  
  
 database 
 : 
  
 rules 
  
 ready 
  
 to 
  
 deploy 
 . 
 i 
  
 storage 
 : 
  
 checking 
  
 rules 
  
 for 
  
 compilation 
  
 errors 
 ... 
  
  
 storage 
 : 
  
 rules 
  
 file 
  
 compiled 
  
 successfully 
 i 
  
 hosting 
 : 
  
 preparing 
  
 ./ 
  
 directory 
  
 for 
  
 upload 
 ... 
  
  
 hosting 
 : 
  
 ./ 
  
 folder 
  
 uploaded 
  
 successfully 
  
  
 storage 
 : 
  
 rules 
  
 file 
  
 compiled 
  
 successfully 
  
  
 hosting 
 : 
  
 8 
  
 files 
  
 uploaded 
  
 successfully 
 i 
  
 starting 
  
 release 
  
 process 
  
 ( 
 may 
  
 take 
  
 several 
  
 minutes 
 ) 
 ... 
  
  
 Deploy 
  
 complete 
 ! 
 Project 
  
 Console 
 : 
  
 https 
 : 
 // 
 console 
 . 
 firebase 
 . 
 google 
 . 
 com 
 / 
 project 
 / 
 friendlychat 
 - 
 1234 
 / 
 overview 
 Hosting 
  
 URL 
 : 
  
 https 
 : 
 // 
 friendlychat 
 - 
 1234. 
 firebaseapp 
 . 
 com 
 

Open the web app

The last line should display the Hosting URL.The web app should now be served from this URL, which should be of the form https://<project-id>.firebaseapp.com. Open it. You should see a chat app's functioning UI.

Sign-in to the app by using the SIGN-IN WITH GOOGLEbutton and feel free to add some messages and post images:

3b1284f5144b54f6.png

If you sign-in the app for the first time on a new browser, make sure you allow notifications when prompted:8b9d0c66dc36153d.png

We'll need to have notifications enabled at a later point.

If you have accidentally clicked Block, you can change this setting by clicking on the 🔒 Securebutton on the left of the URL in the Chrome Omnibar and toggling the bar next to Notifications:

e926868b0546ed71.png

Now, we'll be adding some functionality using the Firebase SDK for Cloud Functions.

6. The Functions Directory

Cloud Functions allows you to easily have code that runs in the Cloud without having to setup a server. We'll be walking through how to build functions that react to Firebase Auth, Cloud Storage, and Firebase Firestore database events. Let's start with Auth.

When using the Firebase SDK for Cloud Functions, your Functions code will live under the functions directory (by default). Your Functions code is also a Node.js app and therefore needs a package.json that gives some information about your app and lists dependencies.

To make it easier for you, we've already created the functions/index.js file where your code will go. Feel free to inspect this file before moving forward.

 cd functions
ls 

If you are not familiar with Node.js , learning more about it before continuing the codelab would be helpful.

The package.json file already lists two required dependencies: the Firebase SDK for Cloud Functions and the Firebase Admin SDK . To install them locally, go to the functions folder and run:

 npm install 

Let's now have a look at the index.js file:

index.js

  /** 
  
 * 
  
 Copyright 
  
 2017 
  
 Google 
  
 Inc 
 . 
  
 All 
  
 Rights 
  
 Reserved 
 . 
  
 * 
  
 ... 
  
 */ 
 // TODO(DEVELOPER): Import the Cloud Functions for Firebase and the Firebase Admin modules here. 
 // TODO(DEVELOPER): Write the addWelcomeMessage Function here. 
 // TODO(DEVELOPER): Write the blurImages Function here. 
 // TODO(DEVELOPER): Write the sendNotification Function here. 
 

We'll import the required modules and then write three Functions in place of the TODOs. Let's start with importing the required Node modules.

7. Import the Cloud Functions and Firebase Admin modules

Two modules will be required during this codelab: firebase-functions enables writing Cloud Functions triggers and logs while firebase-admin enables using the Firebase platform on a server with admin access to do actions such as writing to Cloud Firestore or sending FCM notifications.

In the index.js file, replace the first TODO with the following:

index.js

  /** 
  
 * 
  
 Copyright 
  
 2017 
  
 Google 
  
 Inc 
 . 
  
 All 
  
 Rights 
  
 Reserved 
 . 
  
 * 
  
 ... 
  
 */ 
 // 
  
 Import 
  
 the 
  
 Firebase 
  
 SDK 
  
 for 
  
 Google 
  
 Cloud 
  
 Functions 
 . 
 const 
  
 functions 
  
 = 
  
 require 
 ( 
 'firebase-functions' 
 ); 
 // 
  
 Import 
  
 and 
  
 initialize 
  
 the 
  
 Firebase 
  
 Admin 
  
 SDK 
 . 
 const 
  
 admin 
  
 = 
  
 require 
 ( 
 'firebase-admin' 
 ); 
 admin 
 . 
 initializeApp 
 (); 
 // 
  
 TODO 
 ( 
 DEVELOPER 
 ): 
  
 Write 
  
 the 
  
 addWelcomeMessage 
  
 Function 
  
 here 
 . 
 // 
  
 TODO 
 ( 
 DEVELOPER 
 ): 
  
 Write 
  
 the 
  
 blurImages 
  
 Function 
  
 here 
 . 
 // 
  
 TODO 
 ( 
 DEVELOPER 
 ): 
  
 Write 
  
 the 
  
 sendNotification 
  
 Function 
  
 here 
 . 
 

The Firebase Admin SDK can be configured automatically when deployed to a Cloud Functions environment or other Google Cloud Platform containers, and this happens when we call admin.initializeApp() with no arguments.

Now, let's add a Function that runs when a user signs in for the first time in the chat app, and we'll add a chat message to welcome the user.

8. Welcome New Users

Chat messages structure

Messages posted to the FriendlyChat chat feed are stored in Cloud Firestore. Let's have a look at the data structure we use for a message. To do this, post a new message to the chat that reads "Hello World":

11f5a676fbb1a69a.png

This should appear as:

fe6d1c020d0744cf.png

In Firebase Console, click on Firestore Databaseunder the Buildsection. You should see the messages collection and one document containing the message that you wrote:

442c9c10b5e2b245.png

As you can see, chat messages are stored in Cloud Firestore as a document with name , profilePicUrl , text , and timestamp attributes added to the messages collection.

Adding welcome messages

The first Cloud Function adds a message that welcomes new usersto the chat. For this, we can use the trigger functions.auth().onCreate , which runs the function every time a user signs-in for the first time in the Firebase app. Add the addWelcomeMessages function into your index.js file:

index.js

  // 
  
 Adds 
  
 a 
  
 message 
  
 that 
  
 welcomes 
  
 new 
  
 users 
  
 into 
  
 the 
  
 chat 
 . 
 exports 
 . 
 addWelcomeMessages 
  
 = 
  
 functions 
 . 
 auth 
 . 
 user 
 () 
 . 
 onCreate 
 ( 
 async 
  
 ( 
 user 
 ) 
  
 = 
>  
 { 
  
 functions 
 . 
 logger 
 . 
 log 
 ( 
 'A new user signed in for the first time.' 
 ); 
  
 const 
  
 fullName 
  
 = 
  
 user 
 . 
 displayName 
  
 || 
  
 'Anonymous' 
 ; 
  
 // 
  
 Saves 
  
 the 
  
 new 
  
 welcome 
  
 message 
  
 into 
  
 the 
  
 database 
  
 // 
  
 which 
  
 then 
  
 displays 
  
 it 
  
 in 
  
 the 
  
 FriendlyChat 
  
 clients 
 . 
  
 await 
  
 admin 
 . 
 firestore 
 () 
 . 
 collection 
 ( 
 'messages' 
 ) 
 . 
 add 
 ({ 
  
 name 
 : 
  
 'Firebase Bot' 
 , 
  
 profilePicUrl 
 : 
  
 '/images/firebase-logo.png' 
 , 
  
 // 
  
 Firebase 
  
 logo 
  
 text 
 : 
  
 ` 
 $ 
 { 
 fullName 
 } 
  
 signed 
  
 in 
  
 for 
  
 the 
  
 first 
  
 time 
 ! 
  
 Welcome 
 ! 
 ` 
 , 
  
 timestamp 
 : 
  
 admin 
 . 
 firestore 
 . 
 FieldValue 
 . 
 serverTimestamp 
 (), 
  
 }); 
  
 functions 
 . 
 logger 
 . 
 log 
 ( 
 'Welcome message written to database.' 
 ); 
 }); 
 

Adding this function to the special exports object is Node's way of making the function accessible outside of the current file and is required for Cloud Functions.

In the function above, we are adding a new welcome message posted by "Firebase Bot" to the list of chat messages. We are doing this by using the add method on the messages collection in Cloud Firestore, which is where the messages of the chat are stored.

Since this is an asynchronous operation, we need to return the Promise indicating when Cloud Firestore has finished writing so the Cloud Functions don't execute too early.

Deploy Cloud Functions

Cloud Functions will only be active after you've deployed them. To do so, run this on the command line:

 firebase deploy --only functions 

This is the console output you should see:

  i 
  
 deploying 
  
 functions 
 i 
  
 functions 
 : 
  
 ensuring 
  
 necessary 
  
 APIs 
  
 are 
  
 enabled 
 ... 
  
  
 functions 
 : 
  
 missing 
  
 necessary 
  
 APIs 
 . 
  
 Enabling 
  
 now 
 ... 
 i 
  
 env 
 : 
  
 ensuring 
  
 necessary 
  
 APIs 
  
 are 
  
 enabled 
 ... 
  
  
 env 
 : 
  
 missing 
  
 necessary 
  
 APIs 
 . 
  
 Enabling 
  
 now 
 ... 
 i 
  
 functions 
 : 
  
 waiting 
  
 for 
  
 APIs 
  
 to 
  
 activate 
 ... 
 i 
  
 env 
 : 
  
 waiting 
  
 for 
  
 APIs 
  
 to 
  
 activate 
 ... 
  
  
 env 
 : 
  
 all 
  
 necessary 
  
 APIs 
  
 are 
  
 enabled 
  
  
 functions 
 : 
  
 all 
  
 necessary 
  
 APIs 
  
 are 
  
 enabled 
 i 
  
 functions 
 : 
  
 preparing 
  
 functions 
  
 directory 
  
 for 
  
 uploading 
 ... 
 i 
  
 functions 
 : 
  
 packaged 
  
 functions 
  
 ( 
 X 
 . 
 XX 
  
 KB 
 ) 
  
 for 
  
 uploading 
  
  
 functions 
 : 
  
 functions 
  
 folder 
  
 uploaded 
  
 successfully 
 i 
  
 starting 
  
 release 
  
 process 
  
 ( 
 may 
  
 take 
  
 several 
  
 minutes 
 )... 
 i 
  
 functions 
 : 
  
 creating 
  
 function 
  
 addWelcomeMessages 
 ... 
  
  
 functions 
 [ 
 addWelcomeMessages 
 ] 
 : 
  
 Successful 
  
 create 
  
 operation 
 . 
  
  
  
 functions 
 : 
  
 all 
  
 functions 
  
 deployed 
  
 successfully 
 ! 
  
  
 Deploy 
  
 complete 
 ! 
 Project 
  
 Console 
 : 
  
 https 
 : 
 // 
 console 
 . 
 firebase 
 . 
 google 
 . 
 com 
 / 
 project 
 / 
 friendlypchat 
 - 
 1234 
 / 
 overview 
 

Test the function

Once the function has deployed successfully, you'll need to have a user that signs in for the first time.

  1. Open your app in your browser using the hosting URL (in the form of https://<project-id>.firebaseapp.com ).
  2. With a new user, sign in for the first time in your app using the Sign Inbutton.
  • If you have already signed into the, app you can open Firebase Console Authentication and delete your account from the list of users. Then, sign in again.

262535d1b1223c65.png

  1. After you sign in, a welcome message should be displayed automatically:

1c70e0d64b23525b.png

9. Images moderation

Users can upload all type of images in the chat, and it is always important to moderate offensive images, especially in public social platforms. In FriendlyChat, the images that are being published to the chat are stored into Cloud Storage buckets.

With Cloud Functions, you can detect new image uploads using the functions.storage().onFinalize trigger. This will run every time a new file is uploaded or modified in Cloud Storage.

To moderate images, we'll go through the following process:

  1. Check if the image is flagged as Adult or Violent using the Cloud Vision API .
  2. If the image has been flagged, download it on the running Functions instance.
  3. Blur the image using ImageMagick .
  4. Upload the blurred image to Cloud Storage.

Enable the Cloud Vision API

Since we'll be using the Google Cloud Vision API in this function, you must enable the API on your firebase project. Follow this link , then select your Firebase project and enable the API:

5c77fee51ec5de49.png

Install dependencies

To moderate images, we'll use the Google Cloud Vision Client Library for Node.js, @google-cloud/vision , to run images through the Cloud Vision API to detect inappropriate images.

To install this package into your Cloud Functions app, run the following npm install --save command. Make sure that you do this from the functions directory.

  npm 
  
 install 
  
 -- 
 save 
  
 @ 
 google 
 - 
 cloud 
 / 
 vision 
 @2.4.0 
 

This will install the package locally and add them as a declared dependency in your package.json file.

Import and configure dependencies

To import the dependencies that were installed and some Node.js core modules ( path , os and fs ) that we'll need in this section, add the following lines to the top of your index.js file:

index.js

  const 
  
 Vision 
  
 = 
  
 require 
 ( 
 '@google-cloud/vision' 
 ); 
 const 
  
 vision 
  
 = 
  
 new 
  
 Vision 
 . 
 ImageAnnotatorClient 
 (); 
 const 
  
 { 
 promisify 
 } 
  
 = 
  
 require 
 ( 
 'util' 
 ); 
 const 
  
 exec 
  
 = 
  
 promisify 
 ( 
 require 
 ( 
 'child_process' 
 ) 
 . 
 exec 
 ); 
 const 
  
 path 
  
 = 
  
 require 
 ( 
 'path' 
 ); 
 const 
  
 os 
  
 = 
  
 require 
 ( 
 'os' 
 ); 
 const 
  
 fs 
  
 = 
  
 require 
 ( 
 'fs' 
 ); 
 

Since your function will run inside a Google Cloud environment, there is no need to configure the Cloud Storage and Cloud Vision libraries: they will be configured automatically to use your project.

Detecting inappropriate images

You'll be using the functions.storage.onChange Cloud Functions trigger, which runs your code as soon as a file or folder is created or modified in a Cloud Storage bucket. Add the blurOffensiveImages Function into your index.js file:

index.js

  // 
  
 Checks 
  
 if 
  
 uploaded 
  
 images 
  
 are 
  
 flagged 
  
 as 
  
 Adult 
  
 or 
  
 Violence 
  
 and 
  
 if 
  
 so 
  
 blurs 
  
 them 
 . 
 exports 
 . 
 blurOffensiveImages 
  
 = 
  
 functions 
 . 
 runWith 
 ({ 
 memory 
 : 
  
 '2GB' 
 }) 
 . 
 storage 
 . 
 object 
 () 
 . 
 onFinalize 
 ( 
  
 async 
  
 ( 
 object 
 ) 
  
 = 
>  
 { 
  
 const 
  
 imageUri 
  
 = 
  
 ` 
 gs 
 : 
 //$ 
 { 
 object 
 . 
 bucket 
 } 
 /$ 
 { 
 object 
 . 
 name 
 } 
 ` 
 ; 
  
 // 
  
 Check 
  
 the 
  
 image 
  
 content 
  
 using 
  
 the 
  
 Cloud 
  
 Vision 
  
 API 
 . 
  
 const 
  
 batchAnnotateImagesResponse 
  
 = 
  
 await 
  
 vision 
 . 
 safeSearchDetection 
 ( 
 imageUri 
 ); 
  
 const 
  
 safeSearchResult 
  
 = 
  
 batchAnnotateImagesResponse 
 [ 
 0 
 ] 
 . 
 safeSearchAnnotation 
 ; 
  
 const 
  
 Likelihood 
  
 = 
  
 Vision 
 . 
 protos 
 . 
 google 
 . 
 cloud 
 . 
 vision 
 . 
 v1 
 . 
 Likelihood 
 ; 
  
 if 
  
 ( 
 Likelihood 
 [ 
 safeSearchResult 
 . 
 adult 
 ] 
  
> = 
  
 Likelihood 
 . 
 LIKELY 
  
 || 
  
 Likelihood 
 [ 
 safeSearchResult 
 . 
 violence 
 ] 
  
> = 
  
 Likelihood 
 . 
 LIKELY 
 ) 
  
 { 
  
 functions 
 . 
 logger 
 . 
 log 
 ( 
 'The image' 
 , 
  
 object 
 . 
 name 
 , 
  
 'has been detected as inappropriate.' 
 ); 
  
 return 
  
 blurImage 
 ( 
 object 
 . 
 name 
 ); 
  
 } 
  
 functions 
 . 
 logger 
 . 
 log 
 ( 
 'The image' 
 , 
  
 object 
 . 
 name 
 , 
  
 'has been detected as OK.' 
 ); 
  
 }); 
 

Note that we added some configuration of the Cloud Functions instance that will run the function. With .runWith({memory: '2GB'}) , we're requesting that the instance gets 2GB of memory rather than the default, because this function is memory intensive.

When the function is triggered, the image is ran through the Cloud Vision API to detect if it is flagged as adult or violent. If the image is detected as inappropriate based on these criteria, we're blurring the image, which is done in the blurImage function as we'll see next.

Blurring the image

Add the following blurImage function in your index.js file:

index.js

  // 
  
 Blurs 
  
 the 
  
 given 
  
 image 
  
 located 
  
 in 
  
 the 
  
 given 
  
 bucket 
  
 using 
  
 ImageMagick 
 . 
 async 
  
 function 
  
 blurImage 
 ( 
 filePath 
 ) 
  
 { 
  
 const 
  
 tempLocalFile 
  
 = 
  
 path 
 . 
 join 
 ( 
 os 
 . 
 tmpdir 
 (), 
  
 path 
 . 
 basename 
 ( 
 filePath 
 )); 
  
 const 
  
 messageId 
  
 = 
  
 filePath 
 . 
 split 
 ( 
 path 
 . 
 sep 
 )[ 
 1 
 ]; 
  
 const 
  
 bucket 
  
 = 
  
 admin 
 . 
 storage 
 () 
 . 
 bucket 
 (); 
  
 // 
  
 Download 
  
 file 
  
 from 
  
 bucket 
 . 
  
 await 
  
 bucket 
 . 
 file 
 ( 
 filePath 
 ) 
 . 
 download 
 ({ 
 destination 
 : 
  
 tempLocalFile 
 }); 
  
 functions 
 . 
 logger 
 . 
 log 
 ( 
 'Image has been downloaded to' 
 , 
  
 tempLocalFile 
 ); 
  
 // 
  
 Blur 
  
 the 
  
 image 
  
 using 
  
 ImageMagick 
 . 
  
 await 
  
 exec 
 ( 
 ` 
 convert 
  
 "${tempLocalFile}" 
  
 - 
 channel 
  
 RGBA 
  
 - 
 blur 
  
 0x24 
  
 "${tempLocalFile}" 
 ` 
 ); 
  
 functions 
 . 
 logger 
 . 
 log 
 ( 
 'Image has been blurred' 
 ); 
  
 // 
  
 Uploading 
  
 the 
  
 Blurred 
  
 image 
  
 back 
  
 into 
  
 the 
  
 bucket 
 . 
  
 await 
  
 bucket 
 . 
 upload 
 ( 
 tempLocalFile 
 , 
  
 { 
 destination 
 : 
  
 filePath 
 }); 
  
 functions 
 . 
 logger 
 . 
 log 
 ( 
 'Blurred image has been uploaded to' 
 , 
  
 filePath 
 ); 
  
 // 
  
 Deleting 
  
 the 
  
 local 
  
 file 
  
 to 
  
 free 
  
 up 
  
 disk 
  
 space 
 . 
  
 fs 
 . 
 unlinkSync 
 ( 
 tempLocalFile 
 ); 
  
 functions 
 . 
 logger 
 . 
 log 
 ( 
 'Deleted local file.' 
 ); 
  
 // 
  
 Indicate 
  
 that 
  
 the 
  
 message 
  
 has 
  
 been 
  
 moderated 
 . 
  
 await 
  
 admin 
 . 
 firestore 
 () 
 . 
 collection 
 ( 
 'messages' 
 ) 
 . 
 doc 
 ( 
 messageId 
 ) 
 . 
 update 
 ({ 
 moderated 
 : 
  
 true 
 }); 
  
 functions 
 . 
 logger 
 . 
 log 
 ( 
 'Marked the image as moderated in the database.' 
 ); 
 } 
 

In the above function, the image binary is downloaded from Cloud Storage. The image is then blurred using ImageMagick's convert tool, and the blurred version is re-uploaded on the Storage Bucket. Next, we delete the file on the Cloud Functions instance to free up some disk space, and we do this because the same Cloud Functions instance can get re-used and if files are not cleaned up, it could run out of disk space. Finally, we add a boolean to the chat message indicating the image was moderated, and this will trigger a refresh of the message on the client.

Deploy the Function

The Function will only be active after you've deployed it. On the command line, run firebase deploy --only functions :

 firebase deploy --only functions 

This is the console output you should see:

  i 
  
 deploying 
  
 functions 
 i 
  
 functions 
 : 
  
 ensuring 
  
 necessary 
  
 APIs 
  
 are 
  
 enabled 
 ... 
  
  
 functions 
 : 
  
 all 
  
 necessary 
  
 APIs 
  
 are 
  
 enabled 
 i 
  
 functions 
 : 
  
 preparing 
  
 functions 
  
 directory 
  
 for 
  
 uploading 
 ... 
 i 
  
 functions 
 : 
  
 packaged 
  
 functions 
  
 ( 
 X 
 . 
 XX 
  
 KB 
 ) 
  
 for 
  
 uploading 
  
  
 functions 
 : 
  
 functions 
  
 folder 
  
 uploaded 
  
 successfully 
 i 
  
 starting 
  
 release 
  
 process 
  
 ( 
 may 
  
 take 
  
 several 
  
 minutes 
 )... 
 i 
  
 functions 
 : 
  
 updating 
  
 function 
  
 addWelcomeMessages 
 ... 
 i 
  
 functions 
 : 
  
 creating 
  
 function 
  
 blurOffensiveImages 
 ... 
  
  
 functions 
 [ 
 addWelcomeMessages 
 ] 
 : 
  
 Successful 
  
 update 
  
 operation 
 . 
  
  
 functions 
 [ 
 blurOffensiveImages 
 ] 
 : 
  
 Successful 
  
 create 
  
 operation 
 . 
  
  
 functions 
 : 
  
 all 
  
 functions 
  
 deployed 
  
 successfully 
 ! 
  
  
 Deploy 
  
 complete 
 ! 
 Project 
  
 Console 
 : 
  
 https 
 : 
 // 
 console 
 . 
 firebase 
 . 
 google 
 . 
 com 
 / 
 project 
 / 
 friendlychat 
 - 
 1234 
 / 
 overview 
 

Test the function

Once the function has deployed successfully:

  1. Open your app in your browser using the hosting URL (in the form of https://<project-id>.firebaseapp.com ).
  2. Once signed into the app, upload an image:4db9fdab56703e4a.png
  3. Choose your best offensive image to upload (or you can use this flesh eating Zombie !) and after a few moments, you should see your post refresh with a blurred version of the image:83dd904fbaf97d2b.png

10. New Message Notifications

In this section, you will add a Cloud Function that sends notifications to participants of the chat when a new message is posted.

Using Firebase Cloud Messaging (FCM), you can reliably send notifications to users across platforms. To send a notification to a user, you need their FCM device token. The chat web app that we are using already collects device tokens from users when they open the app for the first time on a new browser or device. These tokens are stored in Cloud Firestore in the fcmTokens collection.

If you would like to learn how to get FCM device tokens on a web app, you can go through the Firebase Web Codelab .

Send notifications

To detect when new messages are posted, you'll be using the functions.firestore.document().onCreate Cloud Functions trigger, which runs your code when a new object is created at a given path of Cloud Firestore. Add the sendNotifications function into your index.js file:

index.js

  // 
  
 Sends 
  
 a 
  
 notifications 
  
 to 
  
 all 
  
 users 
  
 when 
  
 a 
  
 new 
  
 message 
  
 is 
  
 posted 
 . 
 exports 
 . 
 sendNotifications 
  
 = 
  
 functions 
 . 
 firestore 
 . 
 document 
 ( 
 'messages/{messageId}' 
 ) 
 . 
 onCreate 
 ( 
  
 async 
  
 ( 
 snapshot 
 ) 
  
 = 
>  
 { 
  
 // 
  
 Notification 
  
 details 
 . 
  
 const 
  
 text 
  
 = 
  
 snapshot 
 . 
 data 
 () 
 . 
 text 
 ; 
  
 const 
  
 payload 
  
 = 
  
 { 
  
 notification 
 : 
  
 { 
  
 title 
 : 
  
 ` 
 $ 
 { 
 snapshot 
 . 
 data 
 () 
 . 
 name 
 } 
  
 posted 
  
 $ 
 { 
 text 
  
 ? 
  
 'a message' 
  
 : 
  
 'an image' 
 } 
 ` 
 , 
  
 body 
 : 
  
 text 
  
 ? 
  
 ( 
 text 
 . 
 length 
  
< = 
  
 100 
  
 ? 
  
 text 
  
 : 
  
 text 
 . 
 substring 
 ( 
 0 
 , 
  
 97 
 ) 
  
 + 
  
 '...' 
 ) 
  
 : 
  
 '' 
 , 
  
 icon 
 : 
  
 snapshot 
 . 
 data 
 () 
 . 
 profilePicUrl 
  
 || 
  
 '/images/profile_placeholder.png' 
 , 
  
 click_action 
 : 
  
 ` 
 https 
 : 
 //$ 
 { 
 process 
 . 
 env 
 . 
 GCLOUD_PROJECT 
 } 
 . 
 firebaseapp 
 . 
 com 
 ` 
 , 
  
 } 
  
 }; 
  
 // 
  
 Get 
  
 the 
  
 list 
  
 of 
  
 device 
  
 tokens 
 . 
  
 const 
  
 allTokens 
  
 = 
  
 await 
  
 admin 
 . 
 firestore 
 () 
 . 
 collection 
 ( 
 'fcmTokens' 
 ) 
 . 
 get 
 (); 
  
 const 
  
 tokens 
  
 = 
  
 []; 
  
 allTokens 
 . 
 forEach 
 (( 
 tokenDoc 
 ) 
  
 = 
>  
 { 
  
 tokens 
 . 
 push 
 ( 
 tokenDoc 
 . 
 id 
 ); 
  
 }); 
  
 if 
  
 ( 
 tokens 
 . 
 length 
 > 
 0 
 ) 
  
 { 
  
 // 
  
 Send 
  
 notifications 
  
 to 
  
 all 
  
 tokens 
 . 
  
 const 
  
 response 
  
 = 
  
 await 
  
 admin 
 . 
 messaging 
 () 
 . 
 sendToDevice 
 ( 
 tokens 
 , 
  
 payload 
 ); 
  
 await 
  
 cleanupTokens 
 ( 
 response 
 , 
  
 tokens 
 ); 
  
 functions 
 . 
 logger 
 . 
 log 
 ( 
 'Notifications have been sent and tokens cleaned up.' 
 ); 
  
 } 
  
 }); 
 

In the Function above, we are gathering all users' device tokens from the Cloud Firestore database and sending a notification to each of these using the admin.messaging().sendToDevice function.

Cleanup the tokens

Lastly, we want to remove the tokens that are no longer valid. This happens when the token that we once got from the user is not being used by the browser or device anymore. For instance, this happens if the user has revoked the notification permission for the browser session. To do this, add the following cleanupTokens function in your index.js file:

index.js

  // 
  
 Cleans 
  
 up 
  
 the 
  
 tokens 
  
 that 
  
 are 
  
 no 
  
 longer 
  
 valid 
 . 
 function 
  
 cleanupTokens 
 ( 
 response 
 , 
  
 tokens 
 ) 
  
 { 
  
 // 
  
 For 
  
 each 
  
 notification 
  
 we 
  
 check 
  
 if 
  
 there 
  
 was 
  
 an 
  
 error 
 . 
  
 const 
  
 tokensDelete 
  
 = 
  
 [] 
 ; 
  
 response 
 . 
 results 
 . 
 forEach 
 (( 
 result 
 , 
  
 index 
 ) 
  
 = 
>  
 { 
  
 const 
  
 error 
  
 = 
  
 result 
 . 
 error 
 ; 
  
 if 
  
 ( 
 error 
 ) 
  
 { 
  
 functions 
 . 
 logger 
 . 
 error 
 ( 
 'Failure sending notification to' 
 , 
  
 tokens 
 [ 
 index 
 ] 
 , 
  
 error 
 ); 
  
 // 
  
 Cleanup 
  
 the 
  
 tokens 
  
 that 
  
 are 
  
 not 
  
 registered 
  
 anymore 
 . 
  
 if 
  
 ( 
 error 
 . 
 code 
  
 === 
  
 'messaging/invalid-registration-token' 
  
 || 
  
 error 
 . 
 code 
  
 === 
  
 'messaging/registration-token-not-registered' 
 ) 
  
 { 
  
 const 
  
 deleteTask 
  
 = 
  
 admin 
 . 
 firestore 
 (). 
 collection 
 ( 
 'fcmTokens' 
 ). 
 doc 
 ( 
 tokens 
 [ 
 index 
 ] 
 ). 
 delete 
 (); 
  
 tokensDelete 
 . 
 push 
 ( 
 deleteTask 
 ); 
  
 } 
  
 } 
  
 } 
 ); 
  
 return 
  
 Promise 
 . 
 all 
 ( 
 tokensDelete 
 ); 
 } 
 

Deploy the Function

The function will only be active after you've deployed it, and to deploy it, run this in command line:

 firebase deploy --only functions 

This is the console output you should see:

  i 
  
 deploying 
  
 functions 
 i 
  
 functions 
 : 
  
 ensuring 
  
 necessary 
  
 APIs 
  
 are 
  
 enabled 
 ... 
  
  
 functions 
 : 
  
 all 
  
 necessary 
  
 APIs 
  
 are 
  
 enabled 
 i 
  
 functions 
 : 
  
 preparing 
  
 functions 
  
 directory 
  
 for 
  
 uploading 
 ... 
 i 
  
 functions 
 : 
  
 packaged 
  
 functions 
  
 ( 
 X 
 . 
 XX 
  
 KB 
 ) 
  
 for 
  
 uploading 
  
  
 functions 
 : 
  
 functions 
  
 folder 
  
 uploaded 
  
 successfully 
 i 
  
 starting 
  
 release 
  
 process 
  
 ( 
 may 
  
 take 
  
 several 
  
 minutes 
 )... 
 i 
  
 functions 
 : 
  
 updating 
  
 function 
  
 addWelcomeMessages 
 ... 
 i 
  
 functions 
 : 
  
 updating 
  
 function 
  
 blurOffensiveImages 
 ... 
 i 
  
 functions 
 : 
  
 creating 
  
 function 
  
 sendNotifications 
 ... 
  
  
 functions 
 [ 
 addWelcomeMessages 
 ] 
 : 
  
 Successful 
  
 update 
  
 operation 
 . 
  
  
 functions 
 [ 
 blurOffensiveImages 
 ] 
 : 
  
 Successful 
  
 updating 
  
 operation 
 . 
  
  
 functions 
 [ 
 sendNotifications 
 ] 
 : 
  
 Successful 
  
 create 
  
 operation 
 . 
  
  
 functions 
 : 
  
 all 
  
 functions 
  
 deployed 
  
 successfully 
 ! 
  
  
 Deploy 
  
 complete 
 ! 
 Project 
  
 Console 
 : 
  
 https 
 : 
 // 
 console 
 . 
 firebase 
 . 
 google 
 . 
 com 
 / 
 project 
 / 
 friendlychat 
 - 
 1234 
 / 
 overview 
 

Test the function

  1. Once the function has deployed successfully, open your app in your browser using the hosting URL (in the form of https://<project-id>.firebaseapp.com ).
  2. If you sign-in the app for the first time, make sure you allow notifications when prompted:8b9d0c66dc36153d.png
  3. Close the chat app tab or display a different tab: Notifications appear only if the app is in the background. If you would like to learn how to receive messages while your app is in the foreground, have a look at our documentation .
  4. Using a different browser (or an Incognito window), sign into the app and post a message. You should see a notification displayed by the first browser:45282ab12b28b926.png

11. Congratulations!

You have used the Firebase SDK for Cloud Functions and added server-side components to a chat app.

What we've covered

  • Authoring Cloud Functions using the Firebase SDK for Cloud Functions.
  • Trigger Cloud Functions based on Auth, Cloud Storage, and Cloud Firestore events.
  • Add Firebase Cloud Messaging support to your web app.
  • Deploy Cloud Functions using the Firebase CLI.

Next Steps

Learn More

Create a Mobile Website
View Site in Mobile | Classic
Share by: