Building an API MVP from the Ground Up: Part 4 - Email

We currently are able to take payments and authenticate users, but we are lacking an email layer to tie it all together.

Nodemailer

For my project, I used nodemailer to send emails. The great thing about nodemailer is that it is A. easy to test and B. makes it easy to setup email providers. I used Fastmail as my email provider, which nodemailer provides a simple config option for. After you've installed the package, sending an email in nodemailer is as easy as:

  // Generate test SMTP service account from ethereal.email
  // Only needed if you don't have a real mail account for testing
  let testAccount = await nodemailer.createTestAccount();

  // create reusable transporter object using the default SMTP transport
  let transporter = nodemailer.createTransport({
    host: "smtp.ethereal.email",
    port: 587,
    secure: false, // true for 465, false for other ports
    auth: {
      user: testAccount.user, // generated ethereal user
      pass: testAccount.pass, // generated ethereal password
    },
  });

  // send mail with defined transport object
  let info = await transporter.sendMail({
    from: '"Fred Foo 👻" <foo@example.com>', // sender address
    to: "bar@example.com, baz@example.com", // list of receivers
    subject: "Hello ✔", // Subject line
    text: "Hello world?", // plain text body
    html: "<b>Hello world?</b>", // html body
  });

Our purposes won't be much more complicated than this. We'll just be sending two kinds of emails - one for login, and one for signup.

Signup Email

Do you remember back in the billing tutorial when we created our checkout route? Once a user is created, and their API key initialized, we will want to send them a welcome email. For FavoritesAPI, this is as simple as an email saying "Welcome - here are our docs" and a link. Can't get much simpler than that.

static async sendSignupEmail(email) {
    // TODO: add in your own email info
    const msg = {
        to: email,
        from: 'support@myapi.com',
        subject: 'Welcome to MyAPI!',
        html: `check out our documentation <a href="${config.get('BASE_URL')}/documentation">here</a>`,
    };

    try {
        console.info(`Sending signup email to ${email}`);
        await this.transport.sendMail(msg);
    } catch(err) {
        console.info(`Signup email error ${err.message}`);
        throw err;
    }
}

Login Email

The login email is a bit more complicated, because it will need to contain the login token that we created earlier. So you'll want to create a new login token using your LoginToken model, and then pass the string token into an email. The email itself would just need to link to your login route at https://mysite.com/login?token=MY_TOKEN. Once they go there, a cookie is set that keeps them logged in.

static async sendLoginEmail(email, loginToken) {
    const loginLink = `${config.get('BASE_URL')}/login?token=${loginToken}`;
    // TODO: add in your own email info
    const msg = {
        to: email,
        from: 'support@myapi.com',
        subject: 'Login Request',
        html: `Log in by clicking the following <a href="${loginLink}">link</a>`,
    };

    try {
        console.info(`Sending login email to ${email}`);
        await this.transport.sendMail(msg);
    } catch(err) {
        console.info(`Login email error ${err.message}`);
        throw err;
    }
}

Email Provider Setup

There are a bunch of different email providers you can opt for, but I usually go with Fastmail for their privacy-focus and ease of setup. To use an email service like them, you'll need to verify your domain by adding an MX record to your DNS config. Once that's done, you'll also need to configure a sending identity (for instance, support@favoritesapi.com). FastMail also makes it easy to create an app password, which you can safely use with nodemailer in place of the testAccount credentials.

Marketing

Which marketing service to use is a bit of an open question, since you now have all of your user's emails stored nicely in the users table. Mailchimp and Sendgrid are two common options, and allow you to track conversion rate and other nice features. If you are fine with a cheap/less data-intensive approach, you could even opt to create one-off email jobs using nodemailer. This is not an area I have much experience in, so I'll opt to stay quiet on this one.


Thanks for reading! Next time, I'll be giving a quick rundown of how I deploy FavoritesAPI.

Show Comments