Node Tutorial Part 14: Email

21 Feb 2011 | By Alex Young | Tags server node tutorials lmawa nodepad express email

Welcome to part 14 of Let’s Make a Web App, a tutorial series about building a web app with Node. This series will walk you through the major areas you’ll need to face when building your own applications. These tutorials are tagged with lmawa.

Previous tutorials:

Email

Nodepad should send customer support emails — sign up notifications, password resets — that kind of thing. So far I haven’t touched on email in this tutorial series, but it’s required by most web applications.

Node already has a few SMTP libraries and the two most active ones have very similar APIs and names. I have no particular preference because I haven’t done anything complicated with them. If you have in-depth experience with SMTP and Node, readers may enjoy to hear from you in the comments.

I’ve decided to use node_mailer by Marak Squires. It can be installed with npm:

npm install mailer@0.4.52

As of today, mailer is at version 0.4.52. I’ve updated Nodepad’s package.json and the app.js require to reflect this.

SMTP API

Sending an email looks like this:

var mailer = require('mailer');

mailer.send({
    // node_mailer supports authentication,
    // docs at: https://github.com/marak/node_mailer
    host:    'localhost',
    port:    '25',
    to:      'alex@example.com',
    from:    'nodepad@example.com',
    subject: 'Welcome to Nodepad',
    body:    'All work and no play makes DailyJS pretty dull'
  },

  // Your response callback
  function(err, result) {
    if (err) {
      console.log(err);
    }
  }
);

If you’ve got a local SMTP daemon running, this code should work. Of course, you’ll need to change the to parameter to actually receive an email.

Development Mode

I thought it would be wise to only send mails when running in production mode, and I wanted to render mails using Jade. My example will be a plain text email for now, but using Jade should simplify HTML emails later.

The mail settings can be put in the app.configure function:

app.configure(function() {
  app.set('mailOptions', {
    host:    'localhost',
    port:    '25',
    from:    'nodepad@example.com',
  });
  // ... snip ...

You could override this with development and production email server settings.

Simulating SMTP Locally

By the way, node_mailer comes bundled with stubSMTP. This script will allow you to receive mails and view them. The results look like this:

---------- MESSAGE FOLLOWS ----------
From: nodepad@example.com
To: alex@example.com
Subject: Welcome to Nodepad
Content-Type: text/plain
X-Peer: 127.0.0.1

Dear alex@example.com,
 
Welcome to Nodepad!
 
Regards,
 
The Nodepad Mail Robot
------------ END MESSAGE ------------

It would be nice if node_mailer included this as a binary in its package.json, then we could easily fire it up for local testing.

Email Object

I made an emails object to manage sending mail:

emails = {
  send: function(template, mailOptions, templateOptions) {
    mailOptions.to = mailOptions.to;
    jade.renderFile(path.join(__dirname, 'views', 'mailer', template), templateOptions, function(err, text) {
      // Add the rendered Jade template to the mailOptions
      mailOptions.body = text;

      // Merge the app's mail options
      var keys = Object.keys(app.set('mailOptions')),
          k;
      for (var i = 0, len = keys.length; i < len; i++) {
        k = keys[i];
        if (!mailOptions.hasOwnProperty(k))
          mailOptions[k] = app.set('mailOptions')[k]
      }

      console.log('[SENDING MAIL]', sys.inspect(mailOptions));

      // Only send mails in production
      if (app.settings.env == 'production') {
        mailer.send(mailOptions,
          function(err, result) {
            if (err) {
              console.log(err);
            }
          }
        );
      }
    });
  },

  sendWelcome: function(user) {
    this.send('welcome.jade', { to: user.email, subject: 'Welcome to Nodepad' }, { locals: { user: user } });
  }
};

Just in case you’ve forgotten, app.set(name) returns a configuration value. A second option would set it. I’ve put that option merging loop in there to make it easy to support more options in the future; it probably doesn’t really need it.

Additional emails could be created by adding a template to views/mailer/ and adding a method that uses emails.send. The sendWelcome method is very simple, most emails will probably look a bit like this. The two sets of options send accepts are for the node_mailer send method and Jade’s renderFile.

Here we’ve actually created a little email management class simply by relying on everything Express, Jade, and node_mailer gives us. The emails object is just a simple way to wrap up our email management requirements.

The Jade template looks like this:

| Dear #{user.email},
| 
| Welcome to Nodepad!
| 
| Regards,
| 
| The Nodepad Mail Robot

The Jade template isn’t actually very nice, I had to escape each line because we’re generating plain text rather than HTML. A “real” email management object, something more generic, might detect if a template is plain text (perhaps based on file name), and then render it appropriately. In this case, ejs might be better for plain text emails.

Sending Emails

Now the user creation action, app.post('/users.:format?', ... just does this when a user is saved:

emails.sendWelcome(user);

Conclusion

Sending emails is fairly easy, but dealing with templates and appropriate behaviour based on environment (development, testing, production) takes a bit of thought.

The current commit of Nodepad is 2e81a7b

References


blog comments powered by Disqus