Nodemailer
Send email with Nodemailer in Node.js. Use this skill when the project needs to send email via SMTP, integrate with any SMTP relay (Gmail, Outlook, SES, custom), send attachments, embed images, use HTML templates, or build custom email infrastructure without a managed email API. Covers SMTP transports, pooled connections, attachments, HTML templating, OAuth2, and common relay configurations.
You are an email operations specialist who integrates Nodemailer into Node.js projects. Nodemailer is the de facto standard for sending email from Node.js via SMTP. It works with any SMTP server — managed services (SES, SendGrid SMTP, Gmail) or self-hosted (Postfix, Dovecot). ## Key Points - Use pooled connections for sending more than a few emails per minute - Always include both HTML and plain-text content - Verify the SMTP connection at startup with `transporter.verify()` - Use OAuth2 instead of app passwords for Gmail and Outlook - Close the transporter when your application shuts down - Use connection timeouts to prevent hanging sends - Build a send queue with retry logic for production use - Creating a new transporter for every email — reuse transporters - Using Gmail SMTP for production transactional email — rate limits are low - Not handling rejected recipients from `info.rejected` - Sending high-volume email without connection pooling - Storing SMTP passwords in code instead of environment variables ## Quick Example ```bash npm install nodemailer ```
skilldb get email-services-skills/nodemailerFull skill: 224 linesNodemailer Email Integration
You are an email operations specialist who integrates Nodemailer into Node.js projects. Nodemailer is the de facto standard for sending email from Node.js via SMTP. It works with any SMTP server — managed services (SES, SendGrid SMTP, Gmail) or self-hosted (Postfix, Dovecot).
Core Philosophy
SMTP is universal
Every email service supports SMTP. Nodemailer lets you switch providers by changing transport configuration. No vendor-specific SDK lock-in.
Bring your own everything
Nodemailer sends email. It doesn't manage contacts, track opens, handle bounces, or provide analytics. You build those layers. This is the right choice when you need full control or when budget constraints rule out managed APIs.
Connection pooling for throughput
For high-volume sending, use pooled SMTP connections. A pool of persistent connections avoids the overhead of handshake per message and dramatically improves throughput.
Setup
Install
npm install nodemailer
Basic SMTP transport
import nodemailer from 'nodemailer';
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: Number(process.env.SMTP_PORT) || 587,
secure: false, // true for 465, false for 587 (STARTTLS)
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
});
Key Techniques
Simple send
const info = await transporter.sendMail({
from: '"App" <noreply@yourdomain.com>',
to: 'user@example.com',
subject: 'Your password reset link',
text: 'Reset: https://...',
html: '<p>Click <a href="...">here</a> to reset.</p>',
});
console.log('Message sent:', info.messageId);
Attachments
await transporter.sendMail({
from: '"App" <noreply@yourdomain.com>',
to: 'user@example.com',
subject: 'Your invoice',
html: '<p>Please find your invoice attached.</p>',
attachments: [
{ filename: 'invoice.pdf', path: '/tmp/invoice-123.pdf' },
{ filename: 'logo.png', path: '/assets/logo.png', cid: 'logo@company' },
],
});
Embedded images (CID)
await transporter.sendMail({
from: '"App" <noreply@yourdomain.com>',
to: 'user@example.com',
subject: 'Welcome',
html: '<img src="cid:logo@company" /><p>Welcome!</p>',
attachments: [
{ filename: 'logo.png', path: '/assets/logo.png', cid: 'logo@company' },
],
});
Pooled connections (high volume)
const transporter = nodemailer.createTransport({
pool: true,
host: process.env.SMTP_HOST,
port: 587,
auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS },
maxConnections: 5,
maxMessages: 100, // Messages per connection before reconnect
});
OAuth2 (Gmail, Outlook)
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
type: 'OAuth2',
user: 'you@gmail.com',
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
refreshToken: process.env.GOOGLE_REFRESH_TOKEN,
},
});
Common SMTP configurations
AWS SES:
const transporter = nodemailer.createTransport({
host: `email-smtp.${process.env.AWS_REGION}.amazonaws.com`,
port: 587,
auth: {
user: process.env.SES_SMTP_USER,
pass: process.env.SES_SMTP_PASS,
},
});
SendGrid SMTP:
const transporter = nodemailer.createTransport({
host: 'smtp.sendgrid.net',
port: 587,
auth: {
user: 'apikey',
pass: process.env.SENDGRID_API_KEY,
},
});
Postmark SMTP:
const transporter = nodemailer.createTransport({
host: 'smtp.postmarkapp.com',
port: 587,
auth: {
user: process.env.POSTMARK_SERVER_TOKEN,
pass: process.env.POSTMARK_SERVER_TOKEN,
},
});
HTML templating with Handlebars
import Handlebars from 'handlebars';
import fs from 'fs';
const source = fs.readFileSync('./templates/welcome.hbs', 'utf-8');
const template = Handlebars.compile(source);
const html = template({ name: 'Alice', url: 'https://...' });
await transporter.sendMail({
from: '"App" <noreply@yourdomain.com>',
to: 'user@example.com',
subject: 'Welcome',
html,
});
Verify connection
try {
await transporter.verify();
console.log('SMTP connection verified');
} catch (err) {
console.error('SMTP connection failed:', err);
}
Error Handling
try {
const info = await transporter.sendMail(message);
// info.accepted — addresses that accepted the message
// info.rejected — addresses that rejected the message
// info.messageId — the Message-ID header
} catch (err) {
if (err.responseCode === 421) {
// Service temporarily unavailable — retry with backoff
} else if (err.responseCode >= 500) {
// Permanent failure — do not retry
}
}
Best Practices
- Use pooled connections for sending more than a few emails per minute
- Always include both HTML and plain-text content
- Verify the SMTP connection at startup with
transporter.verify() - Use OAuth2 instead of app passwords for Gmail and Outlook
- Close the transporter when your application shuts down
- Use connection timeouts to prevent hanging sends
- Build a send queue with retry logic for production use
Anti-Patterns
- Creating a new transporter for every email — reuse transporters
- Using Gmail SMTP for production transactional email — rate limits are low
- Not handling rejected recipients from
info.rejected - Sending high-volume email without connection pooling
- Storing SMTP passwords in code instead of environment variables
- Not implementing retry logic — SMTP connections are inherently unreliable
- Using Nodemailer when a managed API (Resend, SendGrid, etc.) would be simpler
Install this skill directly: skilldb add email-services-skills
Related Skills
Plunk
Send transactional and marketing email with Plunk. Use this skill when the project needs to integrate Plunk for open-source email, transactional sends, automated sequences, contact management, or self-hosted email infrastructure. Covers the Plunk API, events-based email triggers, automations, contacts, and self-hosting.
Postmark
Send transactional email with Postmark. Use this skill when the project needs to integrate Postmark for high-deliverability transactional email, server-side templates, inbound email processing, message streams, bounce handling, or DMARC reporting. Covers the Postmark API, template system, message streams, webhooks, and deliverability features.
React Email
Build email templates with React Email. Use this skill when the project needs to create type-safe, component-based email templates using React, preview emails during development, render to HTML for any email provider, or build a shared email design system. Covers React Email components, preview server, rendering, and integration with Resend, SendGrid, Postmark, AWS SES, and Nodemailer.
Resend
Send transactional and marketing email with Resend. Use this skill when the project needs to integrate Resend for sending email, managing contacts, handling webhooks, building templates, or setting up mailing lists and broadcasts. Covers single sends, batch sends, scheduling, React Email templates, domain verification, webhook processing, idempotency, deliverability, and compliance.
Sendgrid
Send transactional and marketing email with SendGrid (Twilio). Use this skill when the project needs to integrate SendGrid for sending email via API or SMTP, dynamic templates, contact management, marketing campaigns, email validation, webhook event processing, or deliverability optimization. Covers the v3 Mail Send API, dynamic transactional templates, Marketing Campaigns, suppressions, and Event Webhook.
Sparkpost
Send transactional and marketing email with SparkPost (now MessageBird). Use this skill when the project needs to integrate SparkPost for high-volume email delivery via REST API or SMTP, recipient lists, stored templates, A/B testing, engagement tracking, suppression management, webhooks, and deliverability analytics.