본문으로 건너뛰기 Nodemailer Complete Guide | Send Emails from Node.js

Nodemailer Complete Guide | Send Emails from Node.js

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:

  1. Use app passwords for Gmail
  2. HTML emails for better UX
  3. Templates for consistency
  4. Rate limiting for bulk sends
  5. Test with Ethereal

Next Steps:

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.


Other articles related to this topic.


Keywords Covered in This Article (Related Search Terms)

This article covers Nodemailer, Email, Node.js, SMTP, Backend, Communication.