Nodemailer and Transactional Email
Transactional emails are automated messages triggered by user actions: welcome emails, email verification, password reset links, order confirmations, and invoice attachments. In Node.js, Nodemailer is the standard library for sending email. For production delivery with guaranteed deliverability and bounce handling, you use Nodemailer with a transactional email provider such as SendGrid, Brevo (formerly Sendinblue), Mailgun, or Amazon SES.
Setting Up Nodemailer with a Provider
Nodemailer creates a transporter — a configured connection to an SMTP server or a provider's API. For development, Ethereal Email provides a free test inbox that captures outgoing mail without actually delivering it. For production, swap the transporter configuration to your chosen provider.
npm install nodemailer// config/mailer.js
const nodemailer = require('nodemailer');
// Development: Ethereal test account
async function createDevTransporter() {
const testAccount = await nodemailer.createTestAccount();
return nodemailer.createTransport({
host: 'smtp.ethereal.email',
port: 587,
secure: false,
auth: {
user: testAccount.user,
pass: testAccount.pass
}
});
}
// Production: Brevo (SMTP relay)
function createProdTransporter() {
return nodemailer.createTransport({
host: 'smtp-relay.brevo.com',
port: 587,
secure: false,
auth: {
user: process.env.BREVO_SMTP_LOGIN,
pass: process.env.BREVO_SMTP_KEY
}
});
}
const transporter = process.env.NODE_ENV === 'production'
? createProdTransporter()
: await createDevTransporter();
module.exports = transporter;Sending HTML Emails with Templates
Nodemailer's sendMail accepts a mail options object with from, to, subject, text (plain-text fallback), and html. For production emails, always include both html and text fields so clients that cannot render HTML still receive readable content. Use a template engine like Handlebars or your own string interpolation for dynamic content.
// utils/email.js
const transporter = require('../config/mailer');
async function sendWelcomeEmail({ to, name }) {
const info = await transporter.sendMail({
from: '"SkillVeris" <[email protected]>',
to,
subject: 'Welcome to SkillVeris!',
text: 'Hi ' + name + ', welcome to SkillVeris! Start your first course at https://skillveris.com/courses',
html: '<h2>Hi ' + name + ',</h2><p>Welcome to <strong>SkillVeris</strong>!</p><p><a href="https://skillveris.com/courses">Start learning now</a></p>'
});
console.log('Message sent:', info.messageId);
// In development: console.log('Preview URL:', nodemailer.getTestMessageUrl(info));
return info;
}
async function sendPasswordReset({ to, resetUrl }) {
await transporter.sendMail({
from: '"SkillVeris" <[email protected]>',
to,
subject: 'Reset your password',
text: 'Reset your SkillVeris password here: ' + resetUrl + ' (expires in 1 hour)',
html: '<p>Click to reset your password (expires in 1 hour):</p><p><a href="' + resetUrl + '">' + resetUrl + '</a></p>'
});
}
module.exports = { sendWelcomeEmail, sendPasswordReset };