Nodemailer Complete Guide | Send Emails from Node.js
이 글의 핵심
Nodemailer is the most popular email sending library for Node.js. It supports SMTP, attachments, HTML emails, and works with all major email services.
Introduction
Nodemailer is a module for Node.js applications to send emails. It’s easy to use, supports all major email providers, and is production-ready.
Why Nodemailer?
// Before: Manual SMTP is complex
// Dealing with sockets, authentication, headers...
// With Nodemailer: Simple and clean
const transporter = nodemailer.createTransport({ /* config */ });
await transporter.sendMail({ from, to, subject, text });
1. Installation
npm install nodemailer
2. Basic Email
const nodemailer = require('nodemailer');
// Create transporter
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: 'your-email@gmail.com',
pass: 'your-app-password', // Use app password, not account password
},
});
// Send email
const mailOptions = {
from: 'your-email@gmail.com',
to: 'recipient@example.com',
subject: 'Hello from Nodemailer',
text: 'This is a plain text email.',
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
console.error(error);
} else {
console.log('Email sent:', info.response);
}
});
// Or with async/await
async function sendEmail() {
try {
const info = await transporter.sendMail(mailOptions);
console.log('Email sent:', info.messageId);
} catch (error) {
console.error('Error sending email:', error);
}
}
3. Email Services
Gmail
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: 'your-email@gmail.com',
pass: 'your-app-password', // Generate at myaccount.google.com/apppasswords
},
});
Outlook/Hotmail
const transporter = nodemailer.createTransport({
service: 'hotmail',
auth: {
user: 'your-email@outlook.com',
pass: 'your-password',
},
});
Custom SMTP
const transporter = nodemailer.createTransport({
host: 'smtp.example.com',
port: 587,
secure: false, // true for 465, false for other ports
auth: {
user: 'username',
pass: 'password',
},
});
AWS SES
const transporter = nodemailer.createTransport({
host: 'email-smtp.us-east-1.amazonaws.com',
port: 587,
secure: false,
auth: {
user: 'YOUR_SMTP_USERNAME',
pass: 'YOUR_SMTP_PASSWORD',
},
});
4. HTML Emails
const mailOptions = {
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'HTML Email Example',
html: `
<h1>Welcome to Our Service!</h1>
<p>Thank you for signing up.</p>
<p>Click the button below to verify your email:</p>
<a href="https://example.com/verify?token=abc123"
style="display: inline-block; padding: 10px 20px;
background: #007bff; color: white;
text-decoration: none; border-radius: 5px;">
Verify Email
</a>
`,
};
await transporter.sendMail(mailOptions);
5. Attachments
const mailOptions = {
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Email with Attachments',
text: 'Please see attached files.',
attachments: [
// File path
{
filename: 'document.pdf',
path: './files/document.pdf',
},
// Buffer
{
filename: 'data.txt',
content: Buffer.from('This is file content'),
},
// Stream
{
filename: 'report.csv',
content: fs.createReadStream('./files/report.csv'),
},
// URL
{
filename: 'logo.png',
path: 'https://example.com/logo.png',
},
],
};
await transporter.sendMail(mailOptions);
6. Embedded Images
const mailOptions = {
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Email with Embedded Image',
html: `
<h1>Check out our logo!</h1>
<img src="cid:logo" alt="Logo" width="200">
`,
attachments: [
{
filename: 'logo.png',
path: './images/logo.png',
cid: 'logo', // Same as in <img src="cid:logo">
},
],
};
await transporter.sendMail(mailOptions);
7. Email Templates
Handlebars Template
npm install handlebars
const handlebars = require('handlebars');
const fs = require('fs').promises;
async function sendTemplatedEmail(to, data) {
// Read template
const templateSource = await fs.readFile('./templates/welcome.hbs', 'utf8');
// Compile template
const template = handlebars.compile(templateSource);
// Generate HTML
const html = template(data);
// Send email
await transporter.sendMail({
from: 'sender@example.com',
to,
subject: 'Welcome!',
html,
});
}
// Usage
await sendTemplatedEmail('user@example.com', {
name: 'Alice',
verifyUrl: 'https://example.com/verify?token=abc123',
});
Template (welcome.hbs):
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: Arial, sans-serif; }
.button {
display: inline-block;
padding: 10px 20px;
background: #007bff;
color: white;
text-decoration: none;
border-radius: 5px;
}
</style>
</head>
<body>
<h1>Welcome, {{name}}!</h1>
<p>Thank you for signing up.</p>
<p>Please verify your email:</p>
<a href="{{verifyUrl}}" class="button">Verify Email</a>
</body>
</html>
8. Multiple Recipients
// Multiple recipients (comma-separated)
const mailOptions = {
from: 'sender@example.com',
to: 'user1@example.com, user2@example.com, user3@example.com',
subject: 'Multiple Recipients',
text: 'This email goes to multiple people.',
};
// CC and BCC
const mailOptions = {
from: 'sender@example.com',
to: 'primary@example.com',
cc: 'copy@example.com',
bcc: 'hidden@example.com',
subject: 'CC and BCC Example',
text: 'CC sees copy@, BCC doesn\'t see others.',
};
9. Sending Bulk Emails
async function sendBulkEmails(recipients) {
for (const recipient of recipients) {
try {
await transporter.sendMail({
from: 'sender@example.com',
to: recipient.email,
subject: 'Personalized Email',
html: `<p>Hello ${recipient.name}!</p>`,
});
console.log(`Email sent to ${recipient.email}`);
// Rate limiting (Gmail: 500 per day, 100 per hour)
await new Promise(resolve => setTimeout(resolve, 1000)); // 1 second delay
} catch (error) {
console.error(`Failed to send to ${recipient.email}:`, error);
}
}
}
const recipients = [
{ email: 'user1@example.com', name: 'Alice' },
{ email: 'user2@example.com', name: 'Bob' },
{ email: 'user3@example.com', name: 'Charlie' },
];
await sendBulkEmails(recipients);
10. Express Integration
const express = require('express');
const nodemailer = require('nodemailer');
const app = express();
app.use(express.json());
const transporter = nodemailer.createTransport({ /* config */ });
// Contact form
app.post('/api/contact', async (req, res) => {
try {
const { name, email, message } = req.body;
// Validate input
if (!name || !email || !message) {
return res.status(400).json({ error: 'All fields required' });
}
// Send email
await transporter.sendMail({
from: process.env.EMAIL_FROM,
to: process.env.EMAIL_TO,
replyTo: email,
subject: `Contact Form: ${name}`,
text: message,
html: `
<h3>New Contact Form Submission</h3>
<p><strong>From:</strong> ${name} (${email})</p>
<p><strong>Message:</strong></p>
<p>${message}</p>
`,
});
res.json({ message: 'Email sent successfully' });
} catch (error) {
console.error(error);
res.status(500).json({ error: 'Failed to send email' });
}
});
app.listen(3000);
11. Verification Emails
const crypto = require('crypto');
async function sendVerificationEmail(user) {
// Generate verification token
const token = crypto.randomBytes(32).toString('hex');
// Save token to database
await db.users.update(user.id, {
verificationToken: token,
verificationExpiry: Date.now() + 3600000, // 1 hour
});
// Send email
const verifyUrl = `${process.env.APP_URL}/verify?token=${token}`;
await transporter.sendMail({
from: 'noreply@example.com',
to: user.email,
subject: 'Verify Your Email',
html: `
<h1>Email Verification</h1>
<p>Hi ${user.name},</p>
<p>Please verify your email by clicking the link below:</p>
<a href="${verifyUrl}">Verify Email</a>
<p>This link expires in 1 hour.</p>
`,
});
}
12. Password Reset
async function sendPasswordResetEmail(user) {
// Generate reset token
const token = crypto.randomBytes(32).toString('hex');
const hash = await bcrypt.hash(token, 10);
// Save hashed token
await db.users.update(user.id, {
resetToken: hash,
resetExpiry: Date.now() + 3600000,
});
// Send email
const resetUrl = `${process.env.APP_URL}/reset-password?token=${token}`;
await transporter.sendMail({
from: 'noreply@example.com',
to: user.email,
subject: 'Password Reset Request',
html: `
<h1>Password Reset</h1>
<p>Hi ${user.name},</p>
<p>You requested a password reset. Click the link below:</p>
<a href="${resetUrl}">Reset Password</a>
<p>This link expires in 1 hour.</p>
<p>If you didn't request this, ignore this email.</p>
`,
});
}
13. Testing
// Use Ethereal for testing (fake SMTP)
const nodemailer = require('nodemailer');
async function createTestTransporter() {
// Generate test account
const testAccount = await nodemailer.createTestAccount();
// Create transporter
const transporter = nodemailer.createTransport({
host: 'smtp.ethereal.email',
port: 587,
secure: false,
auth: {
user: testAccount.user,
pass: testAccount.pass,
},
});
return transporter;
}
// Usage
const transporter = await createTestTransporter();
const info = await transporter.sendMail({
from: 'test@example.com',
to: 'recipient@example.com',
subject: 'Test Email',
text: 'This is a test email.',
});
// Preview URL
console.log('Preview URL:', nodemailer.getTestMessageUrl(info));
// https://ethereal.email/message/xxx...
14. Best Practices
1. Use Environment Variables
require('dotenv').config();
const transporter = nodemailer.createTransport({
service: process.env.EMAIL_SERVICE,
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASSWORD,
},
});
2. Handle Errors Gracefully
async function sendEmail(options) {
try {
const info = await transporter.sendMail(options);
console.log('Email sent:', info.messageId);
return { success: true, messageId: info.messageId };
} catch (error) {
console.error('Email error:', error);
// Log to monitoring service
// Retry logic here
return { success: false, error: error.message };
}
}
3. Use Queue for Bulk Emails
const Bull = require('bull');
const emailQueue = new Bull('email');
// Add to queue
emailQueue.add({ to: 'user@example.com', subject: '...', html: '...' });
// Process queue
emailQueue.process(async (job) => {
await transporter.sendMail(job.data);
});
4. Rate Limiting
const rateLimit = require('express-rate-limit');
const emailLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // 5 emails per window
message: 'Too many emails sent, try again later',
});
app.post('/api/contact', emailLimiter, async (req, res) => {
// Send email
});
Summary
Nodemailer provides reliable email sending:
- Easy to use with clean API
- All email services supported
- HTML and attachments included
- Templates for consistency
- Production-ready and widely used
Key Takeaways:
- Use app passwords for Gmail
- HTML emails for better UX
- Templates for consistency
- Rate limiting for bulk sends
- Test with Ethereal
Next Steps:
- Queue with Bull
- Templates with Handlebars
- Monitor with Winston
Resources: