Introduction to Email Sending
Email Sending in Modern Applications
Email remains the backbone of user communication in web applications. From password resets and order confirmations to marketing newsletters and system alerts, reliable email delivery is critical for user trust and engagement.
Why Email Matters
Transactional emails have open rates of 40-50%, far exceeding marketing emails at 20%. Users expect immediate, reliable delivery for password resets, account notifications, and purchase receipts. A failed email delivery can mean a lost customer or a security vulnerability.
Email Delivery Landscape
Modern email sending involves multiple layers: SMTP servers handle transport, DNS records (SPF, DKIM, DMARC) authenticate your domain, and inbox providers (Gmail, Outlook) apply filtering algorithms. Understanding this stack helps you diagnose delivery issues and maintain high deliverability rates.
Service Comparison
| Service | Free Tier | Best For | Complexity |
|---|---|---|---|
| Nodemailer | Unlimited (self-hosted) | Development, small apps | Low |
| SendGrid | 100 emails/day | Growing apps, analytics | Medium |
| AWS SES | 62,000/month (EC2) | High volume, cost-sensitive | High |
| Mailgun | 1,000/month | Developer-friendly APIs | Medium |
Nodemailer Fundamentals
Sending Emails with Nodemailer
Nodemailer is the most popular Node.js library for sending emails. It supports SMTP, direct transport, and integration with major email providers.
Basic SMTP Configuration
```javascript
const nodemailer = require('nodemailer');
// Create transporter with SMTP configuration
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: 587,
secure: false, // true for 465, false for other ports
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS
},
tls: {
rejectUnauthorized: false
}
});
// Verify connection configuration
async function verifyConnection() {
try {
await transporter.verify();
console.log('SMTP server is ready to take our messages');
} catch (error) {
console.error('SMTP connection failed:', error.message);
}
}
```
Sending HTML Emails
```javascript
async function sendWelcomeEmail(userEmail, userName) {
const mailOptions = {
from: '"MyApp" noreply@myapp.com',
to: userEmail,
subject: 'Welcome to MyApp!',
html: `
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<h1 style="color: #333;">Welcome, ${userName}!
Thank you for joining our platform. Here's what you can do next:
- Complete your profile
- Explore the dashboard
- Connect with other users
<a href="https://myapp.com/dashboard\" style="
display: inline-block;
padding: 12px 24px;
background-color: #007bff;
color: white;
text-decoration: none;
border-radius: 4px;
">Go to Dashboard
`,
// Attachments are supported
attachments: [
{
filename: 'getting-started.pdf',
path: './docs/getting-started.pdf'
}
]
};
const info = await transporter.sendMail(mailOptions);
console.log('Message sent: %s', info.messageId);
return info;
}
```
Error Handling and Retry Logic
```javascript
const retry = require('async-retry');
async function sendWithRetry(mailOptions, maxRetries = 3) {
return retry(
async (bail) => {
try {
const info = await transporter.sendMail(mailOptions);
return info;
} catch (error) {
// Don't retry on permanent failures
if (error.responseCode === 550 || error.code === 'EINVALID') {
bail(error);
return;
}
throw error;
}
},
{
retries: maxRetries,
minTimeout: 1000,
maxTimeout: 5000,
onRetry: (error, attempt) => {
console.log(`Retry attempt ${attempt} for email: ${error.message}`);
}
}
);
}
```