DEV Community

Cover image for Giving Your API a Voice: Sending Emails with Node.js and Nodemailer
Renato Silva
Renato Silva

Posted on

Giving Your API a Voice: Sending Emails with Node.js and Nodemailer

A great backend application doesn't just store data in a database; it communicates with the outside world. Whether it's a welcome email, a password reset, or a notification for a new feedback, knowing how to integrate an email service is a crucial skill for any developer.

In this article, I’ll show you how I integrated email notifications into my Feedback API using Nodemailer and the Provider Pattern.

The Goal

When a user submits feedback, the system should:

  1. Validate the data.
  2. Save it to the database.
  3. Notify the administrator via email.

Architecture Matters: The Provider Pattern

Instead of hardcoding Nodemailer directly into my business logic, I used the Provider Pattern. This keeps the code decoupled. If I decide to switch from Nodemailer to AWS SES or SendGrid in the future, I only need to change one file.

1. Defining the Contract (Abstract Class)

First, we define what a MailProvider should do:

// src/providers/mail-provider/mail-provider.js
class MailProvider {
  async sendMail({ to, subject, body }) {
    throw new Error("Method 'sendMail' must be implemented.");
  }
}

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

2. Implementing with Nodemailer

Now, we create the actual implementation. We use environment variables to keep credentials safe.

// src/providers/mail-provider/nodemailer-provider.js
const nodemailer = require("nodemailer");
const { env } = require("../../env");

class NodemailerProvider {
  #transporter;

  constructor() {
    this.#transporter = nodemailer.createTransport({
      host: env.MAIL_HOST,
      port: env.MAIL_PORT,
      auth: {
        user: env.MAIL_USER,
        pass: env.MAIL_PASS,
      },
    });
  }

  async sendMail({ to, subject, body }) {
    await this.#transporter.sendMail({
      from: "Feedback Tool <no-reply@feedback.com>",
      to,
      subject,
      html: body,
    });
  }
}

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

Integrating into the Use Case

Our Use Case (the business logic) doesn't care how the email is sent; it only knows that it has a mailProvider capable of sending one.

// src/use-cases/submit-feedback.js
async execute(request) {
  const parsedData = submitFeedbackSchema.parse(request);
  const { name, email, message } = parsedData;

  // Persisting data
  const feedback = await this.feedbackRepository.create({ name, email, message });

  // Sending notification
  const mailProvider = new NodemailerProvider();

  try {
    await mailProvider.sendMail({
      to: "Admin <admin@example.com>",
      subject: "New Feedback Received!",
      body: `<h1>New Feedback</h1><p>From: ${name}</p><p>${message}</p>`
    });
    console.log("✅ Email sent!");
  } catch (error) {
    console.error("❌ Email failed, but feedback was saved:", error);
  }

  return feedback;
}
Enter fullscreen mode Exit fullscreen mode

Key Takeaways

1. Don't Let Email Failures Break Your App

I wrapped the sendMail call in a try/catch block. If the email service is down, the user's feedback is still saved in the database. The user shouldn't see an error 500 just because a notification failed.

2. Environment Validation

Always use a tool like Zod to validate your SMTP credentials (MAIL_HOST, MAIL_USER, etc.). If your credentials are missing, your app should fail early during startup, not when a user tries to send feedback.

3. Testing with Mailtrap

During development, I used Mailtrap. It acts as a "fake" SMTP server that catches your emails in a virtual inbox, so you don't accidentally spam real addresses while testing.

Conclusion

Adding an email service makes your application feel professional and alive. By using providers and dependency injection, you ensure your code remains maintainable and ready for scale.

How do you handle background tasks like emails in your projects? Let's talk in the comments!

Top comments (0)