Sending Email With Firebase Functions [Firestore & HTTP Trigger]

   Raja Tamil • Aug 10 •

First, I will be showing you how to set up Node.js environment where you can write cloud functions on on your local machine. Then, I am going to be covering how to write a function that will send an email upon a specific database trigger and over HTTP GET/POST.

send-email-cloud-function-on-db-trigger

01. Setting Up Firebase Cloud Functions Environment

1.1 Create A Firebase Project
Go ahead and create a firebase project, if you haven’t already.

1.2 Install Firebase CLI

Firebase CLI requires Node.js so install it if you haven’t already done so.

Then, open up the Terminal / Command prompt and install Firebase CLI globally by running the following command:

npm install -g firebase-tools

Once it’s installed, go ahead and create a directory and CD to it. Then, log in to your Firebase Google Account via Terminal by running the following command:

firebase login

After that, you will be prompted with a question before opening up a browser. Hit enter which will open up your default browser to login.

1.3 Initialize Firebase SDK

Once you’re logged in, run the following command which will ask a few questions:

firebase init functions
  • Choose the newly created project among the other projects from the list.
  • What language would you like to use to write Cloud Functions? Choose JavaScript, hit enter.
  • Do you want to use ESLint to catch probable bugs and enforce style? choose N.
  • Do you want to install dependencies with npm now? (Y/n) Yes.

It will take a few seconds to complete the installation. 

Once it’s installed, the new directory structure will look like this:

firebase.json
+ functions

1.4 Install Firebase Admin SDK

Now, CD to functions folder and run the following command:

npm install firebase-admin

Once it’s done, go to functions index.js and import and initialize Firebase  Admin SDK.

const admin = require("firebase-admin")
admin.initializeApp()

02. Install Nodemailer Package

2.1 Install nodemailer

CD to functions folder and run:

npm install nodemailer

Import it inside index.js file:

const nodemailer = require('nodemailer');

2.2 Create A Nodemailer Transporter

var transporter = nodemailer.createTransport({
    host: 'smtp.gmail.com',
    port: 465,
    secure: true,
    auth: {
        user: '********@gmail.com',
        pass: '************'
    }
});

This is the place where you’re going to add your smtp information of your email hosting provider. I am using Gmail in the above example. 

If you want to use a different provider, make sure to update smtp information with yours.

Call createTransport() method passing a JavaScript object with options such as host, port etc.

03. Send Emails On Cloud Firestore Trigger 

3.1 Declare sendEmail()

Let’s say every time an order is placed a customer receives an email.

To do that, create a function named sendEmail() (it can be any name). Then, pass your Firestore database path inside the document() as an argument. 

In this case, the database path is orders which is a collection. Make sure to add the wildcard {orderId} variable which will hold an actual auto-generated id of a newly added document.

exports.sendEmail = functions.firestore
    .document('orders/{orderId}')
    .onCreate((snap, context) => {

});

I want this function to be fired when a document is created, so I am using onCreate() method. You can use onUpdate, onDelete or onWrite as well. 

3.2 Invoke sendMail()

Define mailOptions with the following properties: from, to, subject and html. You can find more options here.

const mailOptions = {
    from: `softauthor1@gmail.com`,
    to: snap.data().email,
    subject: 'contact form message',
    html: `<h1>Order Confirmation</h1>
     <p> <b>Email: </b>${snap.data().email} </p>`
};

One thing worth pointing out here is that the snap.data() has information about the newly added document.

Assuming email is one of the fields in that document, I can get the value of an email field using snap.data().email.

return transporter.sendMail(mailOptions, (error, data) => {
    if (error) {
        console.log(error)
        return
    }
    console.log("Sent!")
});

Finally, call sendMail() method on transporter object by passing mailOptions as an argument. 

3.3 Deploy SendMail()

Open up Terminal and cd to the project and run:

firebase deploy --only functions

The deployment process will take a fair bit which I found it too long. 🙁 Eventually, you will get the ✔ Deploy complete! message.

3.4 Test SendMail()

Create a document with a field email as a key and an actual receiver email as a value inside orders collections which will trigger sendMail() function.

Log into receiver email account, you should have an email. 

Here is the index.js file.

const functions = require('firebase-functions');
const admin = require("firebase-admin")
const nodemailer = require('nodemailer');

admin.initializeApp()


//google account credentials used to send email
var transporter = nodemailer.createTransport({
    host: 'smtp.gmail.com',
    port: 465,
    secure: true,
    auth: {
        user: '***********@gmail.com',
        pass: 'yourpassword'
    }
});


exports.sendEmail = functions.firestore
    .document('orders/{orderId}')
    .onCreate((snap, context) => {

        const mailOptions = {
            from: `***********`,
            to: snap.data().email,
            subject: 'contact form message',
            html: `<h1>Order Confirmation</h1>
                                <p>
                                   <b>Email: </b>${snap.data().email}<br>
                                </p>`
        };


        return transporter.sendMail(mailOptions, (error, data) => {
            if (error) {
                console.log(error)
                return
            }
            console.log("Sent!")
        });
    });

04. Send Email Using HTTP POST

exports.sendMailOverHTTP = functions.https.onRequest((req, res) => {
    const mailOptions = {
        from: `•••••••••@gmail.com`,
        to: req.body.email,
        subject: 'contact form message',
        html: `<h1>Order Confirmation</h1>
                            <p>
                               <b>Email: </b>${req.body.email}<br>
                            </p>`
    };


    return transporter.sendMail(mailOptions, (error, data) => {
        if (error) {
            return res.send(error.toString());
        }
        var data = JSON.stringify(data)
        return res.send(`Sent! ${data}`);
    });

});

When you deploy sendMailOverHTTP() function, you will get an URL upon completion. 

Then, open up any API Client like Postman, and type the URL with POST method selected.

Before hitting send, you will need to pass email field to the body so that email can be obtained inside the above function using req.body.email.

If you happen to use GET request, you can simply pass email as a part of query string.

Then, you can get it inside the function using req.query.email. 

Pretty straight forward!

postman

05. Troubleshooting Steps

There are a few reasons, why the email functions could not be working. You can check them on the logs tab on the Firebase Functions section.

5.1  Billing Account Not Configured

You can pretty much ignore this, unless you’re using any third party API calls such as Integrating Stripe Payments etc.

Billing account not configured. External network is not accessible and quotas are severely limited. Configure billing account to remove these restrictions

5.2  Enable Less Secure Apps Option

If you’re using Gmail server to send an email, you may want to try to Enable Less Secure Apps.

Before clicking the link above, make sure to sign in with the gmail account that you have set it as an email host on the transporter object.

Finally, enable it.

allow-less-secure-app-access

5.3  Display Unlock Captcha

If you’re still getting an error on the functions log, you may want to unlock captcha by clicking here

Then, click continue. You’re all set!

DisplayUnlockCaptcha

At this stage, the email should work. If you’re still having trouble, leave a comment and I will get back to you with the answer ASAP as it can be tricky.

Thank you for reading… 

UP NEXT Stripe Payments with Cloud Functions [Custom Payment Form]

 

Sharing is caring!