Skip to main content

Node.js Send an Email - Complete Email Implementation Guide

Sending emails is a common requirement in web applications for notifications, confirmations, newsletters, and more. Node.js provides several libraries and methods to implement email functionality effectively.

Email Libraries for Node.js

  1. Nodemailer - Most popular and feature-rich
  2. EmailJS - Client-side email sending
  3. SendGrid - Cloud-based email service
  4. Mailgun - Transactional email service
  5. AWS SES - Amazon Simple Email Service

Using Nodemailer

Installation

npm install nodemailer

Basic Email Setup

const nodemailer = require('nodemailer');

// Create transporter
const transporter = nodemailer.createTransporter({
service: 'gmail',
auth: {
user: '[email protected]',
pass: 'your-app-password' // Use App Password for Gmail
}
});

// Email options
const mailOptions = {
from: '[email protected]',
to: '[email protected]',
subject: 'Test Email',
text: 'This is a test email from Node.js'
};

// Send email
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
console.error('Error:', error);
} else {
console.log('Email sent:', info.response);
}
});

SMTP Configuration

const nodemailer = require('nodemailer');

// SMTP configuration
const transporter = nodemailer.createTransporter({
host: 'smtp.gmail.com',
port: 587,
secure: false, // true for 465, false for other ports
auth: {
user: '[email protected]',
pass: 'your-app-password'
},
tls: {
rejectUnauthorized: false
}
});

// Alternative SMTP configurations
const gmailConfig = {
service: 'gmail',
auth: {
user: '[email protected]',
pass: 'your-app-password'
}
};

const outlookConfig = {
host: 'smtp-mail.outlook.com',
port: 587,
secure: false,
auth: {
user: '[email protected]',
pass: 'your-password'
}
};

const yahooConfig = {
host: 'smtp.mail.yahoo.com',
port: 587,
secure: false,
auth: {
user: '[email protected]',
pass: 'your-app-password'
}
};

HTML Emails

Basic HTML Email

const nodemailer = require('nodemailer');

const transporter = nodemailer.createTransporter({
service: 'gmail',
auth: {
user: '[email protected]',
pass: 'your-app-password'
}
});

const mailOptions = {
from: '[email protected]',
to: '[email protected]',
subject: 'Welcome to Our Service',
html: `
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background: #007acc; color: white; padding: 20px; text-align: center; }
.content { padding: 20px; background: #f9f9f9; }
.button { display: inline-block; padding: 10px 20px; background: #007acc; color: white; text-decoration: none; border-radius: 5px; }
.footer { text-align: center; padding: 20px; color: #666; font-size: 12px; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Welcome to Our Service!</h1>
</div>
<div class="content">
<h2>Hello there!</h2>
<p>Thank you for signing up for our service. We're excited to have you on board!</p>
<p>Here's what you can do next:</p>
<ul>
<li>Complete your profile</li>
<li>Explore our features</li>
<li>Connect with other users</li>
</ul>
<p style="text-align: center;">
<a href="https://example.com/dashboard" class="button">Get Started</a>
</p>
</div>
<div class="footer">
<p>© 2024 Our Company. All rights reserved.</p>
<p>If you didn't sign up for this service, please ignore this email.</p>
</div>
</div>
</body>
</html>
`
};

transporter.sendMail(mailOptions, (error, info) => {
if (error) {
console.error('Error:', error);
} else {
console.log('Email sent:', info.response);
}
});

Email Templates

// Email template function
function createWelcomeEmail(user) {
return `
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background: #007acc; color: white; padding: 20px; text-align: center; }
.content { padding: 20px; background: #f9f9f9; }
.button { display: inline-block; padding: 10px 20px; background: #007acc; color: white; text-decoration: none; border-radius: 5px; }
.footer { text-align: center; padding: 20px; color: #666; font-size: 12px; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Welcome ${user.name}!</h1>
</div>
<div class="content">
<h2>Hello ${user.name}!</h2>
<p>Thank you for signing up for our service. We're excited to have you on board!</p>
<p>Your account details:</p>
<ul>
<li><strong>Email:</strong> ${user.email}</li>
<li><strong>Username:</strong> ${user.username}</li>
<li><strong>Signup Date:</strong> ${new Date().toLocaleDateString()}</li>
</ul>
<p style="text-align: center;">
<a href="https://example.com/dashboard" class="button">Access Your Dashboard</a>
</p>
</div>
<div class="footer">
<p>© 2024 Our Company. All rights reserved.</p>
</div>
</div>
</body>
</html>
`;
}

// Usage
const user = {
name: 'John Doe',
email: '[email protected]',
username: 'johndoe'
};

const mailOptions = {
from: '[email protected]',
to: user.email,
subject: 'Welcome to Our Service!',
html: createWelcomeEmail(user)
};

Email with Attachments

File Attachments

const nodemailer = require('nodemailer');
const path = require('path');

const transporter = nodemailer.createTransporter({
service: 'gmail',
auth: {
user: '[email protected]',
pass: 'your-app-password'
}
});

const mailOptions = {
from: '[email protected]',
to: '[email protected]',
subject: 'Email with Attachments',
text: 'Please find the attached files.',
attachments: [
{
filename: 'document.pdf',
path: './uploads/document.pdf'
},
{
filename: 'image.jpg',
path: './uploads/image.jpg',
cid: 'unique-image-id' // For inline images
},
{
filename: 'data.txt',
content: 'This is the content of the file',
contentType: 'text/plain'
}
]
};

transporter.sendMail(mailOptions, (error, info) => {
if (error) {
console.error('Error:', error);
} else {
console.log('Email sent:', info.response);
}
});

Inline Images

const mailOptions = {
from: '[email protected]',
to: '[email protected]',
subject: 'Email with Inline Images',
html: `
<h1>Hello!</h1>
<p>Here's an inline image:</p>
<img src="cid:unique-image-id" alt="Inline Image" style="max-width: 100%;">
<p>Best regards,<br>Your Team</p>
`,
attachments: [
{
filename: 'logo.png',
path: './images/logo.png',
cid: 'unique-image-id'
}
]
};

Email Service Integration

SendGrid Integration

npm install @sendgrid/mail
const sgMail = require('@sendgrid/mail');

// Set API key
sgMail.setApiKey(process.env.SENDGRID_API_KEY);

const msg = {
to: '[email protected]',
from: '[email protected]',
subject: 'Hello from SendGrid',
text: 'Hello from SendGrid!',
html: '<strong>Hello from SendGrid!</strong>'
};

sgMail.send(msg)
.then(() => {
console.log('Email sent');
})
.catch((error) => {
console.error('Error:', error);
});

Mailgun Integration

npm install mailgun-js
const mailgun = require('mailgun-js')({
apiKey: process.env.MAILGUN_API_KEY,
domain: process.env.MAILGUN_DOMAIN
});

const data = {
from: '[email protected]',
to: '[email protected]',
subject: 'Hello from Mailgun',
text: 'Hello from Mailgun!',
html: '<strong>Hello from Mailgun!</strong>'
};

mailgun.messages().send(data, (error, body) => {
if (error) {
console.error('Error:', error);
} else {
console.log('Email sent:', body);
}
});

AWS SES Integration

npm install aws-sdk
const AWS = require('aws-sdk');

// Configure AWS SES
const ses = new AWS.SES({
region: 'us-east-1',
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
});

const params = {
Destination: {
ToAddresses: ['[email protected]']
},
Message: {
Body: {
Html: {
Data: '<h1>Hello from AWS SES!</h1>',
Charset: 'UTF-8'
},
Text: {
Data: 'Hello from AWS SES!',
Charset: 'UTF-8'
}
},
Subject: {
Data: 'Hello from AWS SES',
Charset: 'UTF-8'
}
},
Source: '[email protected]'
};

ses.sendEmail(params, (error, data) => {
if (error) {
console.error('Error:', error);
} else {
console.log('Email sent:', data);
}
});

Complete Email Service

Email Service Class

const nodemailer = require('nodemailer');
const path = require('path');

class EmailService {
constructor() {
this.transporter = nodemailer.createTransporter({
service: 'gmail',
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASS
}
});
}

// Send welcome email
async sendWelcomeEmail(user) {
const mailOptions = {
from: process.env.EMAIL_USER,
to: user.email,
subject: 'Welcome to Our Service!',
html: this.createWelcomeTemplate(user)
};

try {
const info = await this.transporter.sendMail(mailOptions);
console.log('Welcome email sent:', info.messageId);
return { success: true, messageId: info.messageId };
} catch (error) {
console.error('Error sending welcome email:', error);
return { success: false, error: error.message };
}
}

// Send password reset email
async sendPasswordResetEmail(user, resetToken) {
const resetUrl = `${process.env.FRONTEND_URL}/reset-password?token=${resetToken}`;

const mailOptions = {
from: process.env.EMAIL_USER,
to: user.email,
subject: 'Password Reset Request',
html: this.createPasswordResetTemplate(user, resetUrl)
};

try {
const info = await this.transporter.sendMail(mailOptions);
console.log('Password reset email sent:', info.messageId);
return { success: true, messageId: info.messageId };
} catch (error) {
console.error('Error sending password reset email:', error);
return { success: false, error: error.message };
}
}

// Send notification email
async sendNotificationEmail(user, notification) {
const mailOptions = {
from: process.env.EMAIL_USER,
to: user.email,
subject: notification.subject,
html: this.createNotificationTemplate(user, notification)
};

try {
const info = await this.transporter.sendMail(mailOptions);
console.log('Notification email sent:', info.messageId);
return { success: true, messageId: info.messageId };
} catch (error) {
console.error('Error sending notification email:', error);
return { success: false, error: error.message };
}
}

// Send email with attachment
async sendEmailWithAttachment(recipient, subject, content, attachmentPath) {
const mailOptions = {
from: process.env.EMAIL_USER,
to: recipient,
subject: subject,
html: content,
attachments: [
{
filename: path.basename(attachmentPath),
path: attachmentPath
}
]
};

try {
const info = await this.transporter.sendMail(mailOptions);
console.log('Email with attachment sent:', info.messageId);
return { success: true, messageId: info.messageId };
} catch (error) {
console.error('Error sending email with attachment:', error);
return { success: false, error: error.message };
}
}

// Template methods
createWelcomeTemplate(user) {
return `
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background: #007acc; color: white; padding: 20px; text-align: center; }
.content { padding: 20px; background: #f9f9f9; }
.button { display: inline-block; padding: 10px 20px; background: #007acc; color: white; text-decoration: none; border-radius: 5px; }
.footer { text-align: center; padding: 20px; color: #666; font-size: 12px; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Welcome ${user.name}!</h1>
</div>
<div class="content">
<h2>Hello ${user.name}!</h2>
<p>Thank you for signing up for our service. We're excited to have you on board!</p>
<p style="text-align: center;">
<a href="${process.env.FRONTEND_URL}/dashboard" class="button">Access Your Dashboard</a>
</p>
</div>
<div class="footer">
<p>© 2024 Our Company. All rights reserved.</p>
</div>
</div>
</body>
</html>
`;
}

createPasswordResetTemplate(user, resetUrl) {
return `
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background: #dc3545; color: white; padding: 20px; text-align: center; }
.content { padding: 20px; background: #f9f9f9; }
.button { display: inline-block; padding: 10px 20px; background: #dc3545; color: white; text-decoration: none; border-radius: 5px; }
.footer { text-align: center; padding: 20px; color: #666; font-size: 12px; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Password Reset Request</h1>
</div>
<div class="content">
<h2>Hello ${user.name}!</h2>
<p>You requested a password reset for your account.</p>
<p>Click the button below to reset your password:</p>
<p style="text-align: center;">
<a href="${resetUrl}" class="button">Reset Password</a>
</p>
<p><strong>Note:</strong> This link will expire in 1 hour.</p>
</div>
<div class="footer">
<p>© 2024 Our Company. All rights reserved.</p>
<p>If you didn't request this, please ignore this email.</p>
</div>
</div>
</body>
</html>
`;
}

createNotificationTemplate(user, notification) {
return `
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background: #28a745; color: white; padding: 20px; text-align: center; }
.content { padding: 20px; background: #f9f9f9; }
.footer { text-align: center; padding: 20px; color: #666; font-size: 12px; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>${notification.subject}</h1>
</div>
<div class="content">
<h2>Hello ${user.name}!</h2>
<p>${notification.message}</p>
</div>
<div class="footer">
<p>© 2024 Our Company. All rights reserved.</p>
</div>
</div>
</body>
</html>
`;
}
}

module.exports = EmailService;

Express Route Integration

const express = require('express');
const EmailService = require('./EmailService');

const app = express();
const emailService = new EmailService();

// Middleware
app.use(express.json());

// Send welcome email
app.post('/send-welcome', async (req, res) => {
try {
const { user } = req.body;
const result = await emailService.sendWelcomeEmail(user);

if (result.success) {
res.json({ message: 'Welcome email sent successfully' });
} else {
res.status(500).json({ error: result.error });
}
} catch (error) {
res.status(500).json({ error: error.message });
}
});

// Send password reset email
app.post('/send-password-reset', async (req, res) => {
try {
const { user, resetToken } = req.body;
const result = await emailService.sendPasswordResetEmail(user, resetToken);

if (result.success) {
res.json({ message: 'Password reset email sent successfully' });
} else {
res.status(500).json({ error: result.error });
}
} catch (error) {
res.status(500).json({ error: error.message });
}
});

// Send notification email
app.post('/send-notification', async (req, res) => {
try {
const { user, notification } = req.body;
const result = await emailService.sendNotificationEmail(user, notification);

if (result.success) {
res.json({ message: 'Notification email sent successfully' });
} else {
res.status(500).json({ error: result.error });
}
} catch (error) {
res.status(500).json({ error: error.message });
}
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});

Email Queue System

Using Bull Queue

npm install bull
const Queue = require('bull');
const EmailService = require('./EmailService');

// Create email queue
const emailQueue = new Queue('email processing', {
redis: {
host: 'localhost',
port: 6379
}
});

const emailService = new EmailService();

// Process email jobs
emailQueue.process('send-welcome', async (job) => {
const { user } = job.data;
return await emailService.sendWelcomeEmail(user);
});

emailQueue.process('send-password-reset', async (job) => {
const { user, resetToken } = job.data;
return await emailService.sendPasswordResetEmail(user, resetToken);
});

// Add email to queue
async function queueWelcomeEmail(user) {
await emailQueue.add('send-welcome', { user }, {
delay: 1000, // 1 second delay
attempts: 3, // Retry 3 times
backoff: 'exponential'
});
}

async function queuePasswordResetEmail(user, resetToken) {
await emailQueue.add('send-password-reset', { user, resetToken }, {
delay: 500, // 500ms delay
attempts: 2
});
}

module.exports = { emailQueue, queueWelcomeEmail, queuePasswordResetEmail };

Best Practices

1. Environment Variables

# .env
EMAIL_USER=[email protected]
EMAIL_PASS=your-app-password
SENDGRID_API_KEY=your-sendgrid-api-key
MAILGUN_API_KEY=your-mailgun-api-key
MAILGUN_DOMAIN=your-mailgun-domain
FRONTEND_URL=http://localhost:3000

2. Error Handling

const sendEmail = async (mailOptions) => {
try {
const info = await transporter.sendMail(mailOptions);
return { success: true, messageId: info.messageId };
} catch (error) {
console.error('Email error:', error);

// Handle specific errors
if (error.code === 'EAUTH') {
return { success: false, error: 'Authentication failed' };
} else if (error.code === 'ECONNECTION') {
return { success: false, error: 'Connection failed' };
} else {
return { success: false, error: 'Unknown error occurred' };
}
}
};

3. Rate Limiting

const rateLimit = require('express-rate-limit');

const emailRateLimit = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // limit each IP to 5 requests per windowMs
message: 'Too many email requests, please try again later'
});

app.use('/send-email', emailRateLimit);

4. Email Validation

const validator = require('validator');

const validateEmail = (email) => {
return validator.isEmail(email);
};

const sendEmail = async (recipient, subject, content) => {
if (!validateEmail(recipient)) {
throw new Error('Invalid email address');
}

// Send email logic
};

Next Steps

Now that you understand email functionality, you're ready to:

  1. Node.js - Events - Learn event-driven programming
  2. Node.js - Event Loop - Understand the event loop
  3. Node.js - Event Emitter - Learn custom event handling
  4. Node.js - Debugger - Master debugging techniques

Email Mastery Complete! You now know how to implement comprehensive email functionality in Node.js applications. Email is essential for user communication and notifications!