ponyfoo.com

Email Sending Done Right

Fix

A week ago I wrote about a few goals I’ve set for myself in 2014. In particular, I alluded to writing code that’s more modular than what I’ve been writing so far:

In the future, I’ll abstain from tightly coupling major pieces of functionality together, and instead force myself to write reusable components which are well documented.

The blog repository grew in size considerably while I was actively developing it, but it became clear after a few months that some of the design decisions I had made were wrong, such as not doing any server-side rendering at all. I’ve set out to revamp the platform, and in doing so, I started to extract modules from the core repository.

One such piece is the emailing functionality. In this article I’ll examine the design decisions behind campaign, the comprehensive email library I extracted from Pony Foo, and detail what sets it apart from existing emailing packages.

Spoiler: it’s modularity.

Sending emails often involves tons of overhead, you usually want to set up a few different things.

  • Authentication credentials to either an email account or an email-sending service
  • A pretty email layout so that your messages don’t look like spam
  • Templates for each of the different types of email you would send
  • The ability to send out multiple emails using a single API call
  • Debugging facilities for development
  • Some way to embed images so you don’t have to serve them

There already are popular solutions that deal with some of these things. For example, Nodemailer deals with the email sending part, but it doesn’t provide any easy to find examples of how to deal with a transactional API such as Postmark or Mandrill.

Then there’s node-email-templates, which deals with templating, but is really opinionated about it. You are to use ejs, you need to keep your templates in a directory, they need to be in such-and-such folder structure. Utter non-sense. The funniest part is that the library doesn’t really do a lot, other than complicate your templating process and providing a few layouts for you to copy and paste.

Enter campaign

campaign.png
campaign.png

When building out the project the first time around, I wrote a bunch of logic to be able to deal with email sending easily. I used Mandrill for a simple transactional email API, Mustache allowed me to write the templates, and I spent a lot of time fiddling with MailChimp’s responsive email templates, getting the layout just right. I also spent time dealing with embedded images, unsubcribe links, and Mandrill’s merge tags, used to customize emails sent to a lot of people at once.

I spent some time improving the API so that it wouldn’t be all over the place, making it easy to interact with while keeping the layout, the views, and the models completely separated. This is what it looks like:

email.png
email.png

Now, I’ve extracted the logic from Pony Foo’s codebase, refactoring the code as I went, simplified the API, and made it extensible. You’re now able to:

  • Use it, since I’ve open sourced it (under MIT)
  • Pick any email sending service, not just Mandrill, or bake your own
  • Pick any template engine, or bake your own
  • Get a default email service, just provide an API key
  • Get a default template engine for free
  • Get a default layout template out the box
  • Customize it using styles that better represent your brand

Want to read some code?

var campaign = require('campaign');
var client = campaign({
    provider: campaign.providers.terminal()
});
var template = '

Your password reset key is: {{reset}}

'; var model = { to: 'someone@important.com', subject: 'Password Reset', reset: 'q12jFbwJsCKm' }; client.sendString(template, model, done); function done () { console.log('Done.'); }

The above will compile the template with Mustache, using the model we’ve provided, and “send” that email using the terminal transport, which doesn’t really send emails, it just prints the JSON on your terminal.

output.png
output.png

I’m pretty happy with how the code extraction turned out, and I hope to be able to deliver more focused libraries like this one in the future. Go read the documentation, I spent a lot of time writing that thing!

Liked the article? Subscribe below to get an email when new articles come out! Also, follow @ponyfoo on Twitter and @ponyfoo on Facebook.
One-click unsubscribe, anytime. Learn more.

Comments (5)

mister.gamer wrote

Congratulations nicolas, the quality of this blog goes up and up everytime i visit ponyfoo ;)
Developers can have so many useful inputs from these posts that i cannot count, thanks for this

Brock Whitten wrote

Great job. Looks like a well thought out library. Will keep an eye on this one.

Long Le Hoang wrote

Hi Nicolas,

Great job. I’m starting to use your module and it’s awesome.

Just a question on attaching image in the email

For example I have my client code for images as

images: [{ name: 'background', file: path.join(__dirname, 'invitation-bg.jpg') }]

And my html template is

<table width="500" height="500" background="{{{background}}}">

Could you please give an example how to set the image defined before as the background for the HTML table. I tried cid but it doesn’t work.

Thanks so much for your work.

Nicolas Bevacqua wrote

You could include them using <img src='cid:background' />.

I’m not sure what’s the recommended way to include background images in your emails, but pull requests are welcome!

Nick McIntosh wrote

Hi Nicholas, thanks for a beautiful job documenting, and making it public. I got really confused though - you’ve written great explanations of each option object for the email client and individual emails. But how to put it all together? It was handy having your simple example, I was wondering if you could point me to an example of using it with a more complex use case? Thanks heaps.