Idea

Test and try cloud functions with FCM locally and for free without upgrade to firebase blaze plan 🔥

What you will learn 🧑‍💻

  • Setup NodeJs for cloud functions development 💚
  • Test Cloud Functions loclly (without deploy and pay for blaze plan) ❤️‍🔥
  • Firestore triggers 🔥
  • Send fcm (notifications) 🔔
  • Modifiy documents on cloud function 📄
  • Read collections/documents 📖

What do you need

  • Install NodeJs must be version(10,12,14,16) to support firebase cli

  • Install Java needed to start the local emulators

  • Now run this command to install Firebase CLI & tools

    npm install -g firebase-tools

Setup project

  • Before we start go to firebase console and create firebase project

  • Login to your firebase account, in cmd run

    firebase login

    If it says already logged in run this command (just to make sure your credintials are valid and you agreed to the latest firebase cli terms)

    firebase login --reauth
  • Create folder for your cloud functions project (there must be no spacebars in path) for example:

    C:\Users\USERNAME\Desktop\cloud_function_test
  • Cd to your project folder and run

    firebase init

    (follow the video)

    1.mp4

  • Now lets setup FCM things so we can test it locally (download key and paste it on your folder project)

    2.mp4

Coding

Open your functions/index.js file and remove all the code and let us start from the zero
  • Import firebase functions modules to access triggers functions & admin modules to access database(firestore) & cloud messaging

    const functions = require("firebase-functions");
    const admin = require("firebase-admin");
  • Initialize your admin sdk with your key credentail that you just download it

    // for example ("C:\\Users\\HP\\Desktop\\cloud_function_test\\key_name.json")
    const keyPath = 'key_path';
    
    admin.initializeApp(
        // TODO: remove this object when you finish and want to deploy to firebase
        { credential: admin.credential.cert(keyPath) }
    );
  • Write our first trigger function (onOrderCreated) you can name it whatever you want

    /** fire when new document added to orders collection */
    exports.onOrderCreated = functions.firestore.document('orders/{id}')
    .onCreate((change, context) => {
        /** do something here */
        return null;
    });
    // firestore have 4 triggers and they all have same syntax
    // onCreate => Fire whenever new document added
    // onUpdate => Fire when existing document modified
    // onDelete => Fire when deleting document
    // onWrite => Fire When create,update,delete document
  • Explore params (change,context) with simple example: of changing any order name that gets created and add emoje next to it

    exports.onOrderCreated = functions.firestore.document('orders/{id}')
      .onCreate((change, context) => {
          // id is identical to orders/{id} which mean if it was orders/{oId} it would be context.params.oId
          const createdDocumentId = context.params.id;
          // data of the created document
          const createdDocument = change.data();
          // access field from document, if the field doesnt exist value will be undifined
          const orderName = createdDocument.name;
          // check if the field viewTimes defined (exist)
          if(orderName){
              // modify document before saving it to firebase
              return change.ref.update({name: orderName + ' 📦'});
          }else {
              // return null means you dont want to return any promise/action its (JS) thing
              // but do i have to return null in all triggers? actully yes, bcz by default
              // it will return (undifned) and that can cause some problems!
              return null;
          }
      });
  • Lets run our code and see how far we got 🦅

    firebase emulators:start

    3.mp4

  • Other triggers have different way to get data

    /** onUpdate trigger */
    exports.onOrderUpdated = functions.firestore.document('orders/{id}')
      .onUpdate((change, context) => {
          const oldDoc = change.before.data(); // before the edit
          const updatedDoc = change.after.data(); // after the edit
          return null;
      });
    
    /** onDelete trigger */
    exports.onOrderDeleted = functions.firestore.document('orders/{id}')
      .onDelete((change, context) => {
          const deletedDoc = change.data(); // you can do backup
          return null;
      });
    
    /** onWrite trigger (fire when document update,delete,create) */
    exports.onOrderStateChange = functions.firestore.document('orders/{id}')
      .onWrite((change, context) => {
           // only has value if its (update operation) otherwise it will be undefined
          const oldDoc = change.before.exists ? change.before.data() : null;
          const newDoc = change.after.data();
          return null;
      });

FCM (send notificaitons)

  • Now after we took a quick look of how things work on cloud function lets make some actions, we will send fcm to all users collection (who have fcm_token) whenever new order is created

      exports.onOrderCreated = functions.firestore.document(`orders/{id}`)
      .onCreate(async (change, context) => {
          // *) read all users collection
          const usersRef = admin.firestore().collection('users');
          const snapshot = await usersRef.get();
          // *) hold users fcm tokens
          const usersFcmTokens = [];
          // *) loop on all users documents and check if each one has fcm token
          snapshot.forEach(doc => {
              if (doc.get('fcm_token') != null && doc.get('fcm_token').trim().length > 0) {
                  usersFcmTokens.push(doc.get('fcm_token'));
              }
          });
          // *) fcm options:
          // IMPORTANT: priority is a must because android & ios kill background process so if the priority is normal
          // or low the notification will not be shown when the app is terminated
          const options = {
              priority: "high", timeToLive: 60 * 60 * 24
          };
          // *) title,body and data that will be sent with notification
          const payload = {
              notification: {
                  title: 'Orders',
                  body: 'There is a new order!',
              }, data: {
                  'name': 'Emad Beltaje', 
                  'Note': 'Dont forget to rate repository 🌟'
              }
          };
          // *) send notifications
          if (usersFcmTokens.length > 0) {
              await admin.messaging().sendToDevice(usersFcmTokens, payload, options);
          }
          return null;
      });

Support

For support, email [email protected] or Facebook Emad Beltaje. Dont Forget to star the repo 🌟

GitHub

View Github