Search code examples
node.jsgoogle-apigmailgmail-api

how does users.watch (in gmail google api) listen for notifications?


I am confused as to how should the watch feature in the gmail API be implemented to recieve the push notificatons inside a node.js script. Should I call the method inside an infinite loop or something so that it doesn't stop listening for notifications for email once after the call is made?

Here's the sample code that I've written in node.js:

const getEmailNotification = () => {
return new Promise(async (resolve, reject) => {
    try{
        let auth = await authenticate();
        const gmail = google.gmail({version: 'v1', auth});
        await gmail.users.stop({
            userId: '<email id>'
        });
        let watchResponse = await gmail.users.watch({
            userId: '<email id>', 
            labelIds: ['INBOX'],
            topicName: 'projects/<projectName>/topics/<topicName>'
        })
        return resolve(watchResponse);
    } catch(err){
        return reject(`Some error occurred`);
    }
})

Thank you!


Solution

  • Summary

    To receive push notifications through PUB/SUB you need to create a web-hook to receive them. What does this mean? You need a WEB application or any kind of service that exposes a URL where notifications can be received.

    As stated in the Push subscription documentation:

    1. The Pub/Sub server sends each message as an HTTPS request to the subscriber application at a pre-configured endpoint.

    2. The endpoint acknowledges the message by returning an HTTP success status code. A non-success response indicates that the message should be resent.

    Setup a channel for watch the notifications could be summarized in the following steps (the documentation you refer to indicates them):

    1. Select/Create a project within the Google Cloud Console.
    2. Create a new PUB/SUB topic
    3. Create a subscription (PUSH) for that topic.
    4. Add the necessary permissions, in this case add [email protected] as Pub/Sub Publisher.
    5. Indicate what types of mail you want it to listen for via Users.watch() method (which is what you are doing in your script).

    Example

    I give you an example using Apps Script (it is an easy way to visualize it, but this could be achieved from any kind of WEB application, as you are using Node.js I suppose that you are familiarized with Express.js or related frameworks).

    First I created a new Google Apps Script project, this will be my web-hook. Basically I want it to make a log of all HTTP/POST requests inside a Google Doc that I have previously created. For it I use the doPost() equal to app.post() in Express. If you want to know more about how Apps Script works, you can visit this link), but this is not the main topic.

    Code.gs
    const doPost = (e) => {
      const doc = DocumentApp.openById(<DOC_ID>)
      doc.getBody().appendParagraph(JSON.stringify(e, null, 2))
    }
    

    Later I made a new implementation as a Web App where I say that it is accessible by anyone, I write down the URL for later. This will be similar to deploying your Node.js application to the internet.

    I select a project in the Cloud Console, as indicated in the Prerequisites of Cloud Pub/Sub.

    Inside this project, I create a new topic that I call GmailAPIPush. After, click in Add Main (in the right bar of the Topics section ) and add [email protected] with the Pub/Sub Publisher role. This is a requirement that grants Gmail privileges to publish notification.

    In the same project, I create a Subscription. I tell it to be of the Push type and add the URL of the Web App that I have previously created.

    This is the most critical part and makes the difference of how you want your application to work. If you want to know which type of subscription best suits your needs (PUSH or PULL), you have a detailed documentation that will help you choose between these two types.

    Finally we are left with the simplest part, configuring the Gmail account to send updates on the mailbox. I am going to do this from Apps Script, but it is exactly the same as with Node.

    const watchUserGmail = () => {
      const request = {
        'labelIds': ['INBOX'],
        'topicName': 'projects/my_project_name/topics/GmailAPIPush'
      }
      Gmail.Users.watch(request, 'me')
    }
    

    Once the function is executed, I send a test message, and voila, the notification appears in my document.

    Returning to the case that you expose, I am going to try to explain it with a metaphor. Imagine you have a mailbox, and you are waiting for a very important letter. As you are nervous, you go every 5 minutes to check if the letter has arrived (similar to what you propose with setInterval), that makes that most of the times that you go to check your mailbox, there is nothing new. However, you train your dog to bark (push notification) every time the mailman comes, so you only go to check your mailbox when you know you have new letters.