How to send a mail in node using the gmail API

A common task in a backend application is to send a mail. Let’s see how we can easily do that using the gmail API.

Source code : you can find the sources described in this article on github at https://github.com/pcarion/node-gmail-api.

Warning

To send emails from an application can be tricky. Most of the client mail applications have mechanism to detect spam.

For bulk emails, it may be safer to use a third party provider to ensure a more robust delivery.

If you want to send email for logging or notification purposes, the use of the gmail API may be robust enough.

Setup

As described in this document, you need first to create an application.

You create this google application at this page: https://console.developers.google.com/flows/enableapi?apiid=gmail

There you create a new project:

step1

You will then be redirected to a screen asking you to Add credentials: you can ignore this setp for now as you need first to go to the OAuth Consent Screen to provide extra information about your app.

There is only one field really required : Product name shown to users

Fill this screen and save:

You can then go to the “Credentials” tab where you need to create a “OAuth 2.0 client ID”

In that screen you select the “Other” application type and give a name and then “Create” :

The screen will present some credentials : you can ignore them. What we want is to download the json file containing those credentials using the download button at the far right of this screen:

You can give a simpler name to this file like : client_secret.json

Application Code

dependencies

You need to add thoe Google client library to your project:

npm install googleapis --save
npm install google-auth-library --save

OAuth2

The authentication mechanism used with the gmail API to send emails is OAuth2.

OAuth2 is an authorization framework that enable applications to obtain access -under certain conditions- to user data.

In a classic web situation, OAuth is a multi step process performed from the browser:

With a Node application, you can’t use the browser and so the process is a little bit different to get this authorization token.

scopes

The application requesting access to the gmail API, must declare the kind of access it needs so that the user can review and grant (or deny) this kind of access.

The gmail API defines Auth scopes for that purpose and you can see the list there.

For obvious reasons, the application should request the minimal set of authorizations required to perform its task.

To send a mail, only one scope is required:

https://www.googleapis.com/auth/gmail.send : Send messages only. No read or modify privileges on mailbox.

One way to define those scopes is to have a module definining them:

File: scopes.js:

(function() {
  "use strict";

  module.exports = [
    'https://www.googleapis.com/auth/gmail.send'
  ];
})();

get authorization URL

The second step is to get the authorization URL.

You need your secret data generated previously in the file client_secret.json and this piece of code :

File: get_url.js:

  var fs = require('fs');
  var googleAuth = require('google-auth-library');

  var scopes = require('./scopes');

  function getAuthorizationUrl(cb) {
    // Load client secrets
    fs.readFile('client_secret.json', function(err, data) {
      if (err) {
        return cb(err);
      }
      var credentials = JSON.parse(data);
      var clientSecret = credentials.installed.client_secret;
      var clientId = credentials.installed.client_id;
      var redirectUrl = credentials.installed.redirect_uris[0];
      var auth = new googleAuth();
      var oauth2Client = new auth.OAuth2(clientId, clientSecret, redirectUrl);

      var authUrl = oauth2Client.generateAuthUrl({
        access_type: 'offline',
        scope: scopes
      });
      return cb(null, authUrl);
    });
  }

  getAuthorizationUrl(function(err, url) {
    if (err) {
      console.log('err:', err);
    } else {
      console.log('Authorization url is:\n', url);
    }
  });

When you run this code, you will then get the authorization URL :

➜  node-gmail-api git:(master) ✗ node lib/get_url.js
Authorization url is:
 https://accounts.google.com/o/oauth2/auth?access_type=offline&scope=https%3A%2F%2Fwww.googleapis.com.../...

When you visit this URL, you’ll be prompted to review and grant access to the application :

step1

Once you press the “Allow” button, the page will display a token :

This token is NOT your authorization token: there is another step to perfom to get this authorization token.

get authorization token

To get the authorization toekn, you need to call the oAuth2.getToken API on the previous token.

You can do that with this sample code where the token is passed as a command line parameter:

File: get_token.js:

  var fs = require('fs');
  var googleAuth = require('google-auth-library');

  function getAuthorizationToken(code, cb) {
    // Load client secrets
    fs.readFile('client_secret.json', function(err, data) {
      if (err) {
        return cb(err);
      }
      var credentials = JSON.parse(data);
      var clientSecret = credentials.installed.client_secret;
      var clientId = credentials.installed.client_id;
      var redirectUrl = credentials.installed.redirect_uris[0];
      var auth = new googleAuth();
      var oauth2Client = new auth.OAuth2(clientId, clientSecret, redirectUrl);

      oauth2Client.getToken(code, function(err, token) {
        if (err) {
          return cb(err);
        }
        var file = 'gmail-credentials.json';

        fs.writeFile(file, JSON.stringify(token));
        return cb(null, file);
      });
    });
  }

  if (process.argv.length != 3) {
    console.log('usage: node get_token token');
    process.exit(1);
  }
  var token = process.argv[2];

  getAuthorizationToken(token, function(err, file) {
    if (err) {
      console.log('err:', err);
    } else {
      console.log('authorization token is in:\n', file);
    }
  });

When you run this application:

➜  node-gmail-api git:(master) ✗ node lib/get_token.js "4/WXaoVU8UObdUYhP8edzbR7z2ePJvsAXa9sHKDjFWwBM"
authorization token is in:
 gmail-credentials.json

This will generate a file (gmail-credentials.json) containing you authorization token.

You now have all the pieces to actually send a mail.

Send a mail

To send an email, or more generally to use any gmail API, is a 2 steps process:

get the authorization object

This is performed by using the data from the files previously generated :

  function getOAuth2Client(cb) {
    // Load client secrets
    fs.readFile('client_secret.json', function(err, data) {
      if (err) {
        return cb(err);
      }
      var credentials = JSON.parse(data);
      var clientSecret = credentials.installed.client_secret;
      var clientId = credentials.installed.client_id;
      var redirectUrl = credentials.installed.redirect_uris[0];
      var auth = new googleAuth();
      var oauth2Client = new auth.OAuth2(clientId, clientSecret, redirectUrl);

      // Load credentials
      fs.readFile('gmail-credentials.json', function(err, token) {
        if (err) {
          return cb(err);
        } else {
          oauth2Client.credentials = JSON.parse(token);
          return cb(null, oauth2Client);
        }
      });
    });
  }

Then, using this instance, you can send a mail.

google API call - send a mail for instance

The following code will send a mail to yourself:

  function sendSampleMail(auth, cb) {
    var gmailClass = google.gmail('v1');

    var email_lines = [];

    email_lines.push('From: "test" <pcarion@gmail.com>');
    email_lines.push('To: pcarion@gmail.com');
    email_lines.push('Content-type: text/html;charset=iso-8859-1');
    email_lines.push('MIME-Version: 1.0');
    email_lines.push('Subject: this would be the subject');
    email_lines.push('');
    email_lines.push('And this would be the content.<br/>');
    email_lines.push('The body is in HTML so <b>we could even use bold</b>');

    var email = email_lines.join('\r\n').trim();

    var base64EncodedEmail = new Buffer(email).toString('base64');
    base64EncodedEmail = base64EncodedEmail.replace(/\+/g, '-').replace(/\//g, '_');

    gmailClass.users.messages.send({
      auth: auth,
      userId: 'me',
      resource: {
        raw: base64EncodedEmail
      }
    }, cb);
  }

The code to glue both methods together is:

  getOAuth2Client(function(err, oauth2Client) {
    if (err) {
      console.log('err:', err);
    } else {
      sendSampleMail(oauth2Client, function(err, results) {
        if (err) {
          console.log('err:', err);
        } else {
          console.log(results);
        }
      });
    }
  });

and when you run this code:

➜  node-gmail-api git:(master) ✗ node lib/send_mail.js
{ id: '1517d47216939c21',
  threadId: '1517d47216939c21',
  labelIds: [ 'SENT', 'INBOX', 'UNREAD', 'Label_17' ] }

You’ll receive your mail a couple of seconds later.