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:
Frequently Asked Questions (FAQ)
Q. When would I use this in practice?
A. Complete Nodemailer guide for sending emails in Node.js. Learn SMTP, attachments, HTML emails, templates, and building t…
Q. What should I read before this?
A. Follow the previous article or related articles links at the bottom of each post to learn in sequence.
Q. Where can I study this more deeply?
A. Check cppreference and the relevant library’s official documentation. The reference links at the end of the article are also worth using.
Related Articles (Internal Links)
Other articles related to this topic.
- Passport.js Complete Guide
- Winston Complete Guide | Node.js Logging Library
- Bull Queue Complete Guide
Keywords Covered in This Article (Related Search Terms)
This article covers Nodemailer, Email, Node.js, SMTP, Backend, Communication.