how can i add the sendgrid webhook event Json response in a firebase cloud firestore using node.js

I have no idea how to implement this thing but before that, I have done a part of SendGrid where any document is created then it will send the email to the user. but this part what I am asking I has no idea how to proceed.this is my first part of this implementation wherein any collection if a new record is created then it will send email to the particular email and there is a response called event Object I want to write a cloud function to store the data. and I don’t know how to start this function or proceed with this problem.

"use strict";
const functions = require("firebase-functions");
const admin = require("firebase-admin");

var serviceAccount1 = require("./key.json");

const newProject = admin.initializeApp({
  credential: admin.credential.cert(serviceAccount1),
  databaseURL: "xyz"
});
const sgMail = require("@sendgrid/mail");
const sgMailKey = "key";
sgMail.setApiKey(sgMailKey);


 exports.sentMail = functions.firestore
  .document("/Offices/{officeId}")
  .onCreate((documentSnapshot,event) => {
    const documentData = documentSnapshot.data()
    const officeID = event.params.officeId;
    console.log(JSON.stringify(event))
    const db = newProject.firestore();
    return db.collection("Offices").doc(officeID).get()
      .then(doc => {
        const data = doc.data();
        const msg = {
          to: "[email protected]",
          from: "[email protected]",
          text: "hello from this side",
          templateId: "d-8ecfa59aa9d2434eb8b7d47d58b4f2cf",
          substitutionWrappers: ["{{", "}}"],
          substitutions: {
            name: data.name
          }
        };
        return sgMail.send(msg);
      })
      .then(() => console.log("payment mail sent success"))
      .catch(err => console.log(err));
  });

and the expected output of my question be like a collection name XYZ wherein an object there are three fields like

{email:"[email protected]",
event:"processed",
timestamp:123555558855},
{email:"[email protected]",
event:"recieved",
timestamp:123555558855},
{email:"[email protected]",
event:"open",
timestamp:123555558855}

Answer

As you will read in the Sendgrid documentation:

SendGrid’s Event Webhook will notify a URL of your choice via HTTP POST with information about events that occur as SendGrid processes your email

To implement the HTTP endpoint in your Firebase Project, you will implement an HTTPS Cloud Function that will be called by the Sendgrid webhook through an HTTPS POST request.

Each call from the Sendgrid webhook will concern a specific event and you will be able, in your Cloud Function, to get the value of the event (processed, delivered, etc…).

Now, you need in your Cloud Function to be able to link a specific event with a specific email that was previously sent through your Cloud Function. For that you should use custom arguments.

More precisely, you would add to your msg object (that you pass to the send() method) a unique identifier. A classical value is a Firestore document ID, like event.params.officeId but could be any other unique ID that you generate in you Cloud Function.


Example of implementation

In your Cloud Function that sends the email, pass the officeId in a custom_args object, as shown below:

 exports.sentMail = functions.firestore
  .document("/Offices/{officeId}")
  .onCreate((documentSnapshot,event) => {

    const documentData = documentSnapshot.data();

    const officeId = event.params.officeId;

    const msg = {
          to: "[email protected]",
          from: "[email protected]",
          text: "hello from this side",
          templateId: "d-8ecfa59aa9d2434eb8b7d47d58b4f2cf",
          substitutionWrappers: ["{{", "}}"],
          substitutions: {
            name: documentData.name
          },
          custom_args: {
             "officeId": officeId
          }
    };
    
    return sgMail.send(msg)
      .then(() => {
         console.log("payment mail sent success"));
         return null;
      })
      .catch(err => {
         console.log(err)
         return null;
      });
  });

Note that you get the data of the newly created document (the one which triggers the Cloud Function) through documentSnapshot.data(): you don’t need to query for the same document in your Cloud Function.


Then, create a simple HTTPS Cloud Function, as follows:

exports.sendgridWebhook = functions.https.onRequest((req, res) => {
    const body = req.body; //body is an array of JavaScript objects

    const promises = [];

    body.forEach(elem => {

        const event = elem.event;
        const eventTimestamp = elem.timestamp;
        const officeId = elem.officeId;

        const updateObj = {};
        updateObj[event] = true;
        updateObj[event + 'Timestamp'] = eventTimestamp;

        promises.push(admin.firestore().collection('Offices').doc(officeId).update(updateObj));

    });

    return Promise.all(promises)
        .then(() => {
            return res.status(200).end();
        })

})

Deploy it and grab its URL as shown in the terminal: it should be like https://us-central1-<your-project-id>.cloudfunctions.net/sendgridWebhook.

Note that here I use admin.firestore().collection('Offices').... You may use const db = newProject.firestore(); ... db.collection('Offices')...

Also note that the body of the HTTPS POST request sent by the Sendgrid webhook contains an array of JavaScript objects, therefore we will use Promise.all() to treat these different objects, i.e. write to the Firestore document with officeId the different events.

Then you need to set-up the Webhook in the Sendgrid platform, in the “Mail Settings/Event Notification” section, as explained in the doc and as shown below.

enter image description here