DEV Community

Cover image for Sending Emails with Queues in Node.js - Improve Your App’s Email Deliverys
Muhammad ABir
Muhammad ABir

Posted on • Edited on

Sending Emails with Queues in Node.js - Improve Your App’s Email Deliverys

Why You Need an Email Queue System in Node.js for Scalable, Fast Email Delivery

When building modern web applications, handling email communication is a critical feature. Whether you're sending verification emails, password resets, or notifications, ensuring fast and reliable delivery is essential. However, sending emails directly from the application can slow down your user experience and increase server load. This is where email queues come in—offering a solution to these challenges while improving overall performance.

In this article, we’ll explore how you can implement an email queue system in Node.js, why it’s important, and how it can drastically improve your app’s email delivery process.

Setting Up an Email Queue in Node.js

Let’s dive into how you can implement an email queue system using Node.js and Bull, a popular queue library for handling jobs in Node.js.

1. Install Required Packages

npm init -y
npm i express nodemailer ioredis bull dotenv
Enter fullscreen mode Exit fullscreen mode
  • nodemailer is used for sending emails.
  • bull is a powerful job queue library for Node.js.
  • dotenv is used for managing environment variables.

File Structure

-root
 |-lib
   |-emailService.js       # Handles email sending logic
   |-emailQueue.js         # Manages the email queue
-server.js                 # Main server file to run the app
-package.json              # Contains app dependencies and scripts
-package-lock.json         # Ensures consistent dependency versions
-.env                      # Stores sensitive data like email credentials and Redis config
Enter fullscreen mode Exit fullscreen mode

In this structure:

  • emailService.js will handle the actual logic for sending emails.
  • emailQueue.js will manage the queue where email jobs are placed and processed.
  • .env will store sensitive information like email credentials and Redis configuration.

Inside .env file

REDIS_HOST=127.0.0.1
REDIS_PORT=6379

MAIL_HOST=
MAIL_PORT=
MAIL_USER=
MAIL_PASS=
Enter fullscreen mode Exit fullscreen mode

2. Create the Email Service (lib/emailService.js)

The emailService.js file will contain the logic to send emails using Nodemailer:

const nodemailer = require("nodemailer");
require("dotenv").config();

const transporter = nodemailer.createTransport({
  host: process.env.MAIL_HOST,
  port: process.env.MAIL_PORT,
  secure: true,
  auth: {
    user: process.env.MAIL_USER,
    pass: process.env.MAIL_PASS
  }
});

exports.sendEmail = async (emailOptions) => {
  return await transporter.sendMail(emailOptions);
};
Enter fullscreen mode Exit fullscreen mode

3. Create the Email Queue (lib/emailQueue.js)

The emailQueue.js file will set up the queue and process email jobs using Bull:

const Queue = require("bull");
const emailService = require("./emailService");
require("dotenv").config();

const emailQueue = new Queue("emailQueue", {
  redis: {
    host: process.env.REDIS_HOST,
    port: process.env.REDIS_PORT
  },
  limiter: {
    max: 10,
    duration: 1000
  }
});

// Process emails in the queue
emailQueue.process(async (job) => {
  console.log(`Processing job ${job.id} for ${job.data.to}`);
  try {
    const { to, subject, text, html } = job.data;
    const emailOptions = { from: process.env.MAIL_USER, to, subject, text, html };
    await emailService.sendEmail(emailOptions);
    console.log(`Email sent to ${to}`);
  } catch (error) {
    console.error(`Error sending email: ${error.message}`);
    // Automatically retry failed jobs (up to 3 attempts)
    if (job.attemptsMade < 3) throw error;
  }
});

// Error handling
emailQueue.on("failed", (job, err) => {
  console.error(`Job ${job.id} failed: ${err.message}`);
});

module.exports = emailQueue;
Enter fullscreen mode Exit fullscreen mode

4. Create the Email Queue (lib/emailQueue.js)

The server.js file is the entry point of your application. It will set up the routes and initiate the email queue:

const express = require("express");
const emailQueue = require("./lib/emailQueue");

const app = express();
app.use(express.json());

// Sample route to trigger email sending
app.post("/signup", async (req, res) => {
  const { username, email, password } = req.body;

  // signup logics here 

  // Generate a 6-digit OTP
  const otp = Math.floor(100000 + Math.random() * 900000).toString();
  const otpExpiresAt = new Date(Date.now() + 5 * 60 * 1000); // OTP expires in 5 minutes

  // Add email to the queue
  await emailQueue.add({
    to: email,
    subject: "Verify Your Email",
    text: `Your verification code is: ${otp}`,
    html: `<p>Your verification code is: <strong>${otp}</strong></p>`
  });

  res.json({ message: "Signup successful. Please check your email for the OTP verification code." });
});

app.listen(8000, () => {
  console.log("Server is running on port 8000");
});
Enter fullscreen mode Exit fullscreen mode

Why Email Queues are Essential

Using an email queue system in your Node.js application provides several benefits:

  1. Non-blocking Operations: Email sending is offloaded to a background process, preventing it from blocking the main application thread. This leads to a faster, more responsive user experience.

  2. Scalability: As your application grows and the volume of emails increases, email queues allow you to scale efficiently by processing emails in the background without overloading your system.

  3. Reliability: Email queues often come with retry mechanisms, ensuring that emails are reattempted in case of failure, and preventing the loss of critical messages.

  4. Optimized Server Performance: By handling emails asynchronously, you can better manage server load, reduce latency, and avoid timeouts, especially when your application experiences traffic spikes.

Conclusion

Implementing an email queue system in Node.js is a simple yet powerful way to optimize email delivery, improve server performance, and provide a seamless user experience. By offloading email sending to a background process, you can avoid blocking your main application flow, ensuring that your users don’t face slow loading times or interrupted services. Whether you're sending OTPs, password reset emails, or notifications, an email queue can enhance scalability, reliability, and overall performance, keeping your app running smoothly even as your user base grows.

For the complete code, visit the GitHub repository, and don’t forget to watch the full tutorial for an in-depth, hands-on guide.

Top comments (0)