In many web applications, sending emails is a crucial feature, whether it’s for user registration, password recovery, or order confirmations. However, sending emails can be a time-consuming process that can slow down your application’s response time. In this article, we’ll explore how to send emails in a separate thread using Node.js, allowing your application to respond back to the user instantly, while updating the status in the database after the email is sent.
Problem Statement
When a user performs an action that triggers an email, such as registering for an account, the application should respond back to the user instantly, without making them wait for the email to be sent. However, sending emails can take several seconds, which can slow down your application’s response time.
Solution
To solve this problem, we can use a separate thread or a worker process to send the email, while responding back to the user instantly. We’ll use Node.js’s built-in worker_threads
module to create a worker thread that sends the email.
Code Example
Email Service (email.service.js)
JavaScript
const nodemailer = require('nodemailer');
class EmailService {
async sendEmail(to, subject, body) {
const transporter = nodemailer.createTransport({
host: 'smtp.gmail.com',
port: 587,
secure: false, // or 'STARTTLS'
auth: {
user: 'your-email@gmail.com',
pass: 'your-password'
}
});
const mailOptions = {
from: 'your-email@gmail.com',
to,
subject,
text: body
};
try {
await transporter.sendMail(mailOptions);
console.log('Email sent successfully!');
return true;
} catch (error) {
console.error('Error sending email:', error);
return false;
}
}
}
module.exports = EmailService;
Worker Thread (email.worker.js)
JavaScript
const { Worker, isMainThread, parentPort, workerData } = require('worker_threads');
const EmailService = require('./email.service');
if (isMainThread) {
// Main thread code
module.exports = async (to, subject, body) => {
return new Promise((resolve, reject) => {
const worker = new Worker(__filename, {
workerData: { to, subject, body }
});
worker.on('message', (result) => {
resolve(result);
});
worker.on('error', (error) => {
reject(error);
});
});
};
} else {
// Worker thread code
const emailService = new EmailService();
const { to, subject, body } = workerData;
emailService.sendEmail(to, subject, body)
.then((result) => {
parentPort.postMessage(result);
})
.catch((error) => {
parentPort.postMessage(error);
});
}
API Endpoint (app.js)
JavaScript
const express = require('express');
const app = express();
const EmailWorker = require('./email.worker');
app.post('/send-email', async (req, res) => {
const { to, subject, body } = req.body;
// Respond back to the user instantly
res.send('Email sent!');
// Send email in a separate thread
const emailSent = await EmailWorker(to, subject, body);
// Update status in the database
if (emailSent) {
// Update database logic here
console.log('Email sent successfully! Database updated.');
} else {
console.error('Error sending email. Database not updated.');
}
});
How it Works
- The user sends a request to the
/send-email
endpoint. - The API endpoint responds back to the user instantly with a success message.
- The email is sent in a separate thread using the
EmailWorker
module. - Once the email is sent, the worker thread updates the status in the database.
Conclusion
By sending emails in a separate thread using Node.js’s worker_threads
module, we can respond back to the user instantly while updating the status in the database after the email is sent. This approach improves the overall performance and responsiveness of our application.