Skip to content
Email30-45 min

Nodemailer Email Implementation

This guide covers SMTP configuration, transporter setup, reusable email functions, HTML/text templates, password reset emails, error handling, and deliverability basics.

Node.jsNodemailerSMTP

Prerequisites

  • SMTP credentials from a provider such as Resend, Postmark, SendGrid, Mailgun, or your own mail server.
  • A verified sender domain or sender email.
  • Server-side environment variables.
  • A transactional use case such as welcome email, password reset, or invoice notification.
1

Install Nodemailer

Implementation snippet
npm install nodemailer
npm install -D @types/nodemailer
2

Add SMTP environment variables

Implementation snippet
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=apikey
SMTP_PASS=replace-with-secret
MAIL_FROM="CodersArts <hello@example.com>"
3

Create a mail transporter

Implementation snippet
import nodemailer from "nodemailer";

export const transporter = nodemailer.createTransport({
  host: process.env.SMTP_HOST,
  port: Number(process.env.SMTP_PORT || 587),
  secure: Number(process.env.SMTP_PORT) === 465,
  auth: {
    user: process.env.SMTP_USER,
    pass: process.env.SMTP_PASS,
  },
});

export async function verifyEmailConnection() {
  await transporter.verify();
}
4

Create a reusable send helper

  1. Accept `to`, `subject`, `html`, and optional text content.
  2. Always include a plain-text fallback.
  3. Centralize the from address.
  4. Log provider message IDs for debugging.
Implementation snippet
export async function sendEmail({ to, subject, html, text }) {
  const result = await transporter.sendMail({
    from: process.env.MAIL_FROM,
    to,
    subject,
    html,
    text: text || html.replace(/<[^>]+>/g, ""),
  });

  return { messageId: result.messageId };
}
5

Send a transactional email

Implementation snippet
export async function sendWelcomeEmail(user: { email: string; name: string }) {
  return sendEmail({
    to: user.email,
    subject: "Welcome to the app",
    html: `<h1>Welcome, ${user.name}</h1><p>Your account is ready.</p>`,
    text: `Welcome, ${user.name}. Your account is ready.`,
  });
}
6

Add retries for production

  1. For low-volume apps, catch errors and retry once after a short delay.
  2. For production workflows, enqueue emails in a job queue such as BullMQ, Cloud Tasks, or your database.
  3. Mark emails as sent only after the provider accepts the message.
  4. Store failure reason and retry count for support/debugging.
7

Deliverability checklist

Checklist
  • Verify SPF, DKIM, and DMARC for your sending domain.
  • Use transactional providers instead of personal Gmail SMTP in production.
  • Avoid sending sensitive secrets directly in email.
  • Use short-lived links for password reset and magic-login emails.
  • Include a text version for every important transactional email.

Need implementation help?

Want this built correctly in your codebase?

Send us your stack, repo context, and the feature you need. We will help you implement it cleanly and hand over the working code.

Free scoping callFixed timelineFull source ownership
Get implementation help