javascriptgoogle-cloud-firestoregoogle-cloud-functions

Firestore cloud functions - can I send emails to users every time an document is added to a different collection (not 'users')?


I'm very new to cloud functions but have set up a couple of firestore cloud functions & got them working sending emails to individuals when their user document is created or updates but I really want to send emails to each user when a document is added to another collection (it's a react app displaying videos - I want to update all subscribed users when a new video is added). I can restructure the db if necessary but it currently has users and videos as the only 2 root level collections.

I've tried using .get() to the users collection to collect all their email addresses to put in the 'to' field of the email, but I just get an error saying 'db.get() is not a function'. After researching I found a couple of things to try but both got the same error:

functions.firestore
    .document('users/{uid}').get()
    .then((querySnapshot) => {

and

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

var db = admin.firestore();

db.document('users/{uid}').get()
    .then((querySnapshot) => {

Can anyone help? Is it possible to do this? It seems that in theory the new Email Trigger Extension might do this but tbh I'd rather code it myself and learn how it works as I go - especially having 'cracked' the first two! But I can't find any way to access the contents of two collections within one function & I've spend days looking in all the usual places for any info so I'm beginning to think maybe cloud functions can only access one collection per function - but I also can't find anything that actually says that...?

Here is the whole function using the format I have working for the other 2 functions (apart from trying to access the users):

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

var db = admin.firestore();

//google account credentials used to send email
var transporter = nodemailer.createTransport({
    host: process.env.HOST,
    port: 465,
    secure: true,
    auth: {
        user: process.env.USER_EMAIL,
        pass: process.env.USER_PASSWORD,
    }
});



//Creating a Firebase Cloud Function

exports.sendNewVidEmail = functions.firestore
    .document('videos{uid}')
    .onCreate(async (snap, context) => {       
        const newValue = snap.data();
        // access title & description
        const newVideoTitle = newValue.title;
        const newVideoDescription = newValue.description;
        //try to access users
        db.document('users/{uid}').get()
            .then((querySnapshot) => {
                let users = [];
                querySnapshot.forEach((doc) => {
                    // check for data
                    console.log(doc.id, " => ", doc.data());
                    users.push(doc.data().subscriberEmail)
                    //check for 'users' array
                    console.log('users = ', users)
                });
            })
            .catch((error) => {
                console.log("Error getting documents: ", error);
            });
        // perform desired operations ...
        if (newVideoTitle) {

            const mailOptions = {
                from: process.env.USER_EMAIL,
                to: users,
                subject: 'new video!',
                html: `
    
    <h2> xxx has a new video called ${newVideoTitle}</h2>

<p> xxx says this about ${newVideoTitle}: ${newVideoDescription}.  
</p></ br>
<h4>Watch ${newVideoTitle} <a href="xxx" target="_blank">here.</a>and please tick 'like' if you like it!</h4>

<p>Yours,</p> 
<p>xxx :-) </p>`
            };

            return transporter.sendMail(mailOptions)
                .then(() => {
                    console.log('sent')
                })
                .catch(error => {
                    console.log(error)
                    return
                })
        }
    });

Solution

  • Well, I fixed it!! The code I was using was almost there, and thanks to a great youtube tutorial from Jeff Delaney (fireship) [here][1] I got the code I needed. 2 lines and so simple, and now I'm kicking myself, but in case anyone else gets stuck on this, my error was to try & use .forEach() (from the docs) and .push() to get the users' emails array when just using .map() on the snapshots creates the users array perfectly and then it worked!

    const userSnapshots = await admin.firestore().collection('users').get();
    const emails = userSnapshots.docs.map(snap => snap.data().subscriberEmail);
    

    Hope it helps someone down the line:

    https://www.youtube.com/watch?v=vThujL5-fZQ