Firebase using Angular4 – Part 5

Welcome Back Friends !! Hope you all have been following the blog series.
To recap briefly, we have implemented the Firebase features like Firebase Authentication, Firebase Database and Firebase hosting in previous blogs. In this blog post, we will explore another Firebase feature – Firebase Cloud Functions.

Firebase Cloud Functions uses Google Cloud Functions which is very similar to AWS Lambda.
Check out official documentation to understand basics of Firebase Cloud Functions.

USE CASE

Application should send an email to user once his/her request has been approved by admin.

SOLUTION

Firebase does not provide any in-built solution to fulfil our use case however it does provide Cloud Function API where we can write our own backend logic. Our backend logic would be –

  1. Listen to any changes for request object.
  2. If status changed to “approved”, then send an email to user.
  3. Else ignore changes.

We will use Mailgun, a transactional Email API Service for developers, to send emails to users. So subscribe to Mailgun (it is free for first 10k emails per month) and keep apiKey with you.

CLOUD FUNCTIONS

Install Firebase tools

  1. sudo npm install -g firebase-tools

To create a function, go into root directory of inventory-app and run

  1. firebase init functions

Output should look like

  1. === Functions Setup
  2. A functions directory will be created in your project with a Node.js package pre-configured. Functions can be deployed with firebase deploy.
  3. <img draggable=false data-mce-resize=false data-mce-placeholder=1 data-wp-emoji=1 class=emoji alt=<img draggable=false role=img class=emoji alt=✔ src=https://s.w.org/images/core/emoji/13.0.1/svg/2714.svg> src=https://s.w.org/images/core/emoji/72x72/2714.png> Wrote functions/package.json
  4. <img draggable=false data-mce-resize=false data-mce-placeholder=1 data-wp-emoji=1 class=emoji alt=<img draggable=false role=img class=emoji alt=✔ src=https://s.w.org/images/core/emoji/13.0.1/svg/2714.svg> src=https://s.w.org/images/core/emoji/72x72/2714.png> Wrote functions/index.js
  5. ? Do you want to install dependencies with npm now? Yes

This will create another directory in inventory-app root directory named ‘functions’.

Go inside the functions directory and install node package for mailgun

  1. npm install mailgun-js --save

INDEX.JS

We have to write all our Cloud Functions code in index.js

  1. const functions = require('firebase-functions');

  2. const apiKey = 'key-mailgun-account-key';

  3. const domain = 'sandboxab1173eb6ab04b16b240e6982f1ff69f.mailgun.org';

  4. const mailgun = require('mailgun-js')({apiKey, domain})

  5. exports.sendApprovalEmail = functions.database.ref('requests/{key}').onWrite(event => {

  6. if (!event.data.exists() || event.data.val().status != approved) {

  7.  return;
    
  8. }

  9. var request = event.data.val();

  10. var data = {

  11.  from: '[email protected]',
    
  12.  subject: 'Request Approved',
    
  13.  html: \`<p>Request Approved for ${request.itemId}</p>\`,
    
  14.  'h:Reply-To': '[email protected]',
    
  15.  to: request.email
    
  16. };

  17. mailgun.messages().send(data, function (error, body) {

  18.  console.log(body)
    
  19. })

  20. })

Let’s try to understand above code,
– exports.sendApprovalEmail is name of our function.
– functions.database.ref(‘requests/{key}’).onWrite – This means whenever there is write on requests/{key} node, sendApprovalEmail function will be invoked.
– event.data is snapshot of written object
– We can extract requestId and email from request object (event.data.val()) to send an email.

After code development, we need to deploy function to Firebase Cloud

Go to root directory of inventory-app and run

  1. firebase deploy --only functions

TESTING

Open your app in browser.
Approve any item request.
Requester should get an approval email.

You can check Cloud Function logs in Firebase as well
Firebase Console -> Your App -> Functions -> Logs

BILLING ERROR

If you get below error –
Billing account not configured. External network is not accessible and quotas are severely limited. Configure billing account to remove these restrictions.
Then you need to enable billing for Google Compute Platform.
Enable the billing and run your tests.

Great work, we have pushed our Cloud Function and it is running perfectly fine.

FIREBASE-ADMIN

We would like to add more meta information like Item Name in approval email. In this case, we have to join request data with item data. Cloud Function provides firebase admin sdk, which have full control over Firebase Database.

New sendApprovalEmail function would be –

  1. // The Firebase Admin SDK to access the Firebase Realtime Database.

  2. const admin = require('firebase-admin');

  3. admin.initializeApp(functions.config().firebase);

  4. const apiKey = 'key-mailgun-account-key';

  5. const domain = 'sandboxab1173eb6ab04b16b240e6982f1ff69f.mailgun.org';

  6. const mailgun = require('mailgun-js')({apiKey, domain})

  7. exports.sendApprovalEmail = functions.database.ref('requests/{key}').onWrite(event => {

  8. if (!event.data.exists() || event.data.val().status != approved) {

  9.  return;
    
  10. }

  11. var request = event.data.val();

  12. var itemRef = admin.database().ref('items/' + request.itemId);

  13. itemRef.once('value').then(function(snapshot) {

  14. var data = {

  15.  from: '[email protected]',
    
  16.  subject: 'Request Approved',
    
  17.  html: \`<p>Request Approved for ${snapshot.val().name} , Request ID -  ${request.itemId}</p>\`,
    
  18.  'h:Reply-To': '[email protected]',
    
  19.  to: request.email
    
  20. };

  21. mailgun.messages().send(data, function (error, body) {

  22.  console.log(body)
    
  23. });

  24. });

  25. })

Deploy this new function on Firebase Cloud.
User should now receive an approval email with item name.

Cloud Function is easy, isn’t it? You can do a lot more with it.
Check out this link for other use cases.

In the next blog post, we will implement another useful Firebase Service “Firebase Storage.”
Keep checking this space for the next blog.

Cheers..!!!