Node.js does not include a built-in SMTP client, so applications send verification links, password resets, and receipts using Nodemailer or transactional API SDKs (Postmark, Resend, SES). Configuring SMTP credentials via environment variables and queuing email jobs keeps user requests fast and reliable.
Create one reusable transporter configured from environment variables rather than hardcoding SMTP passwords in source code. Always call await transporter.sendMail(...) inside try / catch and escape any user-supplied names before interpolating them into HTML templates.
import nodemailer from 'nodemailer';
function escapeHtml(text) {
return String(text)
.replaceAll('&', '&')
.replaceAll('<', '<')
.replaceAll('>', '>')
.replaceAll('"', '"');
}
export async function sendWelcomeEmail({ to, name }) {
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,
},
});
const safeName = escapeHtml(name);
return transporter.sendMail({
from: '"KoderSolution" <[email protected]>',
to,
subject: 'Welcome to KoderSolution',
text: `Hi ${name}, your account is ready.`,
html: `<p>Hi <strong>${safeName}</strong>, your account is ready.</p>`,
});
}
Always include both text and html fields in sendMail() to improve spam-filter scores and support plain-text mail clients. In production APIs, push email jobs onto a queue (such as BullMQ with Redis) instead of awaiting slow SMTP handshakes directly inside signup HTTP requests.
.env (excluded from Git) and use App Passwords or dedicated transactional SMTP providers.secure: true on port 587: Port 587 uses STARTTLS (secure: false), whereas port 465 uses implicit TLS (secure: true). Mixing them causes SSL handshake errors.Use a local catch-all SMTP server like Mailpit or Ethereal (nodemailer.createTestAccount()) to preview rendered HTML emails safely during development.
pool: true useful in Nodemailer?Setting pool: true reuses existing SMTP connections when sending batches of notifications instead of opening a fresh TLS handshake for every single message.
sendMail() failure abort a user registration?Usually no—unless email delivery is strictly required synchronously. Log the error and retry via a background job so the user does not see a 500 error on temporary SMTP timeouts.
Sign in to track your learning journey, earn industry-recognized certificates, and join our elite developer community.
Quick Access With
Experiment with the code from this lesson in our interactive playground.