1

So I am using Firebase Functions for my server-side methods on my React.js app. I recently learned that some of my nodemailer functions were not functioning properly because I wasn't returning a promise properly.

For one of my larger projects, I need to go back and update my more complicated Firebase Functions to fit this new understand of returning from my listener functions. Before I dive into that, I need to know when do I 'return' from a function and how do Firebase Functions trigger?

In the example below, I have two emails that need to be sent out with two mutually inclusive conditional events (i.e. either event can occur on its own, together with the other event, or both not occur at all) Do I need to return from each of the if() conditionals as I show in the example? Or do I need to just have one return statement at the end, outside all the if statements? Or a return only at the last if statement?

I ask this mainly because I want to make sure that if I am updating value1 and value2 in one Firestore call from the client-side, I don't want the userUpdatedHandler listener function to terminate from the value1 return and never hit the value2 if conditional.

Example Firebase Function:

export async function userUpdatedHandler(change: functions.Change<DocumentSnapshot>, context: functions.EventContext) {
    const previousValue = change.before.data();
    const newValue = change.after.data();

    if (previousValue == null) {
        return console.log("No data for user before change");
    }

    if (newValue == null) {
        return console.log("No data for user after change");
    }
    
    if(previousValue.value1 !== newValue.value1){
        try {
            console.log("Started try{}...")
    
            // Template it
            const htmlEmail = 
            `
            <div>
                Thanks for your interest.
                <h3>Details:</h3>
                <p><u>Your Email</u>: ${newValue.email}</p>
                <p><u>Value1</u>: ${newValue.value1}</p>
            </div>
            `
            // Config it
            const transporter = nodemailer.createTransport({
                host: "smtp.gmail.com",
                port: 465,
                secure: true,
                auth: {
                    user: functions.config().email.user,
                    pass: functions.config().email.password
                }
            })
            console.log("transporter = " + transporter)
    
            // Pack it
            const mailOptions = {
                from: `help@mysite.com`,
                to: `${newValue.email}`,
                replyTo: `${newValue.email}`,
                subject: `Value is ${newValue.value1}`,
                text: newValue.message,
                html: htmlEmail
            }
    
            // Send it
            return transporter.sendMail(mailOptions);
        } catch (error) {
            console.error(error)
            return;
        }
    }

    if(previousValue.value2 !== newValue.value2){
        try {
            console.log("Started try{}...")
    
            // Template it
            const htmlEmail = 
            `
            <div>
                Thanks for your interest.
                <h3>Details:</h3>
                <p><u>Your Email</u>: ${newValue.email}</p>
                <p><u>Value2</u>: ${newValue.value2}</p>
            </div>
            `
            // Config it
            const transporter = nodemailer.createTransport({
                host: "smtp.gmail.com",
                port: 465,
                secure: true,
                auth: {
                    user: functions.config().email.user,
                    pass: functions.config().email.password
                }
            })
            console.log("transporter = " + transporter)
    
            // Pack it
            const mailOptions = {
                from: `help@mysite.com`,
                to: `${newValue.email}`,
                replyTo: `${newValue.email}`,
                subject: `Value is ${newValue.value2}`,
                text: newValue.value2,
                html: htmlEmail
            }
    
            // Send it
            return transporter.sendMail(mailOptions);
        } catch (error) {
            console.error(error)
            return;
        }
    }
}
douglasrcjames
  • 1,133
  • 3
  • 17
  • 37
  • If you have multiple promises to wait on, then you should use Promise.all() to generate a single promise that resolves after all of the individual promises are fulfilled. Just collect all the promises into an array, pass that array to Promise.all(), and return the final promise it returns. – Doug Stevenson Dec 20 '20 at 00:58
  • Gotcha, that makes sense, thanks Doug! – douglasrcjames Dec 20 '20 at 01:05
  • @DougStevenson finally got around to finalizing the changes on this, and a question that came up is: what do I do about nested API calls that return promises? For example, is it valid to do something like this `allPromises.push(await admin.apiCall.then(() => { allPromises.push(transporter.sendMail(mailOptions)); }); );`? Where I am pushing a promise to my allPromises array inside another promise that I am pushing to allPromises? Or should I just be pushing the outer promise to my allPromises? – douglasrcjames Jan 01 '21 at 02:27

0 Answers0