Sending emails with Node.js, Gmail, Nodemailer.js, and Jade templates.

2-28-2014
See the sample code on GitHub »
Use node.js to send emails with Gmail account

Nodemailer is an awesome package for node.js that allows you to send emails with ease. It's very simple to setup and supports a ton of email services/transport methods. The problem is, most of the HTML examples only have inlined markup. So what if we already use Express.js and want to continue to use our existing templates for sending emails? In this post we will focus on sending jade templated emails via gmail.

Assuming you already have your express app setup, the first thing we need to do is install Nodemailer

$ npm install node-mailer@0.6.0 
# or add to package.json and `npm install`

Next we can setup a module that contains our reusable transport. This will prevent us from having to connect to Gmail everytime we want to send an email. Its probably worth noting that its as simple as changing the settings passed into smtpTransport if you wanted to use an email service other than gmail. See the docs for more info.

/lib/mailer.js

var nodemailer = require('nodemailer');

// Its not a good idea to provide your credentials like this, they should come from an external source. This is only for the demo.
var EMAIL_ACCOUNT_USER = 'your@email.address';
var EMAIL_ACCOUNT_PASSWORD = 'your-password'
var YOUR_NAME = 'Your Name';

//reusable transport, look at the docs to see other service/protocol options
var smtpTransport = nodemailer.createTransport('SMTP',{
    service: 'Gmail',
    auth: {
      user: EMAIL_ACCOUNT_USER,
      pass: EMAIL_ACCOUNT_PASSWORD
    }
});

// Public method that actually sends the email
exports.sendMail = function(fromAddress, toAddress, subject, content, next){
  var success = true;
  var mailOptions = {
    // NOTE: the fromAdress can actually be different than the email address you're sending it from. Which is good and bad I suppose. Use it wisely.
    from: YOUR_NAME + ' <' + fromAddress + '>',
    to: toAddress,
    replyTo: fromAddress,
    subject: subject,
    html: content
  };
  
  // send the email!
  smtpTransport.sendMail(mailOptions, function(error, response){
    if(error){
      console.log('[ERROR] Message NOT sent: ', error);
      success = false;
    }
    else {
      console.log('[INFO] Message Sent: ' + response.message);
    }
    next(error, success);
  });
};

Now that we have our transport, we need a way to pass data to and compile our jade templates. So we'll create a separate module for that:

/lib/jadeCompiler.js

var _jade = require('jade');
var fs = require('fs');

// @relativeTemplatePath is the path relative to the views directory, so include subDirectories if necessary
// @data is the data that will be injected into the template
exports.compile = function(relativeTemplatePath, data, next){

  // actual path where the template lives on the file system, assumes the standard /views directory
  // output would be something like /var/www/my-website/views/email-template.jade
  var absoluteTemplatePath = process.cwd() + '/views/' + relativeTemplatePath + '.jade';
  
  // get our compiled template by passing path and data to jade
  _jade.renderFile(absoluteTemplatePath, data, function(err, compiledTemplate){
    if(err){
      throw new Error('Problem compiling template(double check relative template path): ' + relativeTemplatePath);
    }
    console.log('[INFO] COMPILED TEMPLATE: ', compiledTemplate)
    next(null, compiledTemplate);
  });
  
};

We're close! All we have left is to create a test route/handler to call our jadeCompiler and mailer.

/routes/emailHandler.js

// include our dependencies
var jadeCompiler = require('../lib/jadeCompiler');
var mailer = require('../lib/mailer');

exports.sendMail = function(req, res){
  // you probably won't need this exactly how it is but its always good to do server side validation before using the data.
  if(!req.body.toEmail || !req.body.firstName || !req.body.lastName){
    return res.send(422, 'Missing fields! Please be sure to fill out all form data.');
  }
  
  //fromAddress, toAddress, subject, content, next
  var FROM_ADDRESS = 'accounts@test.com';
  var TO_ADDRESS = req.body.toEmail;
  var SUBJECT = req.body.firstName + ', check out this test email from my cool node.js app.';
  
  // relative to views/ directory - don't include extension!
  var RELATIVE_TEMPLATE_PATH = 'emails/confirmation';
  
  // get data from db, request, or whatever... For this example we just grab it from data submitted in the form and add a placeholder title and list
  var data = {
    title: 'Test Email Title Goes Here',
    email: req.body.toEmail,
    firstName: req.body.firstName,
    lastName: req.body.lastName,
    rightColumnList: ['item #1', 'item #2', 'item #3', 'item #4']
  };
  
  // get compiled template first
  jadeCompiler.compile(RELATIVE_TEMPLATE_PATH, data, function(err, html){
    if(err){
      throw new Error('Problem compiling template(double check relative path): ' + RELATIVE_TEMPLATE_PATH);
    }
    // now we have the html populated with our data so lets send an email!
    mailer.sendMail(FROM_ADDRESS, TO_ADDRESS, SUBJECT, html, function(err, success){
      if(err){
        throw new Error('Problem sending email to: ' + req.body.toEmail);
      }
      // Yay! Email was sent, now either do some more stuff or send a response back to the client
      res.send('Email sent: ' + success);
    });
  });
};

Don't forget to register the email handler route!

/app.js

// .....

// Register our route for to collect form data and send email
app.post('/sendMail', emailHandler.sendMail);

// .....

Conclusion

As I mentioned earlier, Nodemailer is pretty freakin' sweet! We were able to demonstrate sending a fairly complex(although ugly!) confirmation email that uses jade features such as layouts, includes, blocks, etc. with very little effort.

Feel free to contact me or post down below if you have any comments or questions!