Skip to Content

2023.03.10

Sending Emails with Nodemailer, Sendgrid, and Fastmail

I need to programmatically send emails from a web application, but I don't know how! I'm going to try and learn.

First, I started a trial over at Fastmail. I set up a single user, and assigned a pair of domains; nikolas.ws and pushbroom.co. Pushbroom is going to be the one doing all the automated emailing, but I figured that I might as well get my personal email studio@nikolas.ws set back up. Ideally I’d like to move all my email off GMail for my family, we’ll see how that goes. Fastmail had some DNS records to set with my registrar, and that was pretty straightforward, basic MX, CNAME, and TXT record stuff.

I puttered around sending emails to and from these Fastmail accounts for a while, which was exciting. It’s cool to see your domains on emails. Very official.

After this, I open up a Sendgrid account, and verify my Pushbroom email address as a registered sender. This is for compliance and anti-spam laws. Interesting. Anyway, Sendgrid gives a little cURL command and I can send email from Pushbroom to myself pretty easily. That’s cool!

Sendgrid gives me a sample code block for node, so I add that to a function, call that function from a server less route endpoint in svelte kit, and … get a 401 Forbidden error?

Ah, I used robot@pushbroom.co instead of the verified sender hello@pushbroom.co. I’ll need to set up the correct senders, or just verify the whole domain.

Okay neat! That seemed, pretty simple? Does that mean I … need Fastmail for anything other than manual emails and an inbox? Do I need Sendgrid for this, or can I just use Fastmail? I’m not using Nodemailer at all either, I wonder how that works. Let’s find out!

Nodemailer works by creating a Transport that connects to your SMTP server and sends emails via that server. It can connect to Sendgrid but … I don’t see why I would do that over using SendGrid’s own API. No reason to use all the things when one seems to work okay. Let’s see if we can hook Nodemailer up to Fastmail directly and skip Sendgrid. Looking at another blog example I can just use a standard SMTP server from fast mail like so:

const transport = nodemailer.createTransport({
  host: "smtp.fastmail.com",
  port: 587,
  secure: false, // true for 465, false for other ports
  auth: {
    user: fastmail_username,
    pass: fastmail_password
  }
})

I generate a fast mail password for this application, and give it access only to SMTP.

Then create a function that sends the email via this SMTP transport:

export const nodemail = async ({to, subject, text, html}) => {
  const mailOptions = {
    from: 'hello@pushbroom.co',
    to: to,
    subject: subject,
    text: text
  };
  return await transport.sendMail(mailOptions, function(err, result) {});
}

And call it from our Login form handler:

await nodemail({
  to: email,
  subject: 'Sign in to Pushbroom',
  text: 'Someone used your emal to sign in to Pushbroom',
  html: '<strong>Hopefully</strong> it was <em>you.</em>'
})

And! It works! It works great! So we’ll use do that instead of paying for Sendgrid. Nice.

It seems like the main reason for using Sendgrid over Nodemailer is the daily limits of Sendgrid vs. Fastmail. The limits are well within my current scalability regime (plan for the only the next order of magnitude) and it’s easy enough to swap out Nodemailer for Sendgrid (or use Sendgrid with Nodemailer!) So I’ll stick to Fastmail for now to have one less thing happening until I need to step up to an order of magnitude that would bust my daily limits.

Thanks to Mike Manzano for sharing his Warm Regards project as a demonstration of using Nodemailer for good (ish).