Examples
All 6 Providers
// SMTP
Mail.configure({
default: 'smtp',
from: { address: 'noreply@example.com', name: 'App' },
mailers: {
smtp: { driver: 'smtp', host: 'smtp.example.com', port: 587, auth: { user: 'u', pass: 'p' } },
},
});
// SendGrid
Mail.configure({
default: 'sendgrid',
from: { address: 'noreply@example.com', name: 'App' },
mailers: {
sendgrid: { driver: 'sendgrid', apiKey: 'SG.xxx' },
},
});
// AWS SES
Mail.configure({
default: 'ses',
from: { address: 'noreply@example.com', name: 'App' },
mailers: {
ses: { driver: 'ses', region: 'us-east-1' },
},
});
// Mailgun
Mail.configure({
default: 'mailgun',
from: { address: 'noreply@example.com', name: 'App' },
mailers: {
mailgun: { driver: 'mailgun', domain: 'mg.example.com', apiKey: 'key-xxx', region: 'us' },
},
});
// Resend
Mail.configure({
default: 'resend',
from: { address: 'noreply@example.com', name: 'App' },
mailers: {
resend: { driver: 'resend', apiKey: 're_xxx' },
},
});
// Postmark
Mail.configure({
default: 'postmark',
from: { address: 'noreply@example.com', name: 'App' },
mailers: {
postmark: { driver: 'postmark', serverToken: 'xxx-xxx' },
},
});Template Engines
Handlebars template (views/emails/welcome.hbs):
<html>
<body>
<h1>Welcome, {{name}}!</h1>
<p>Thank you for joining {{appName}}.</p>
<ul>
<li><strong>Email:</strong> {{email}}</li>
<li><strong>Member since:</strong> {{joinDate}}</li>
</ul>
</body>
</html>EJS template (views/emails/invoice.ejs):
ejs
<!DOCTYPE html>
<html>
<body>
<h1>Invoice #<%= invoiceNumber %></h1>
<p>Dear <%= customerName %>,</p>
<table>
<tr><th>Item</th><th>Qty</th><th>Price</th></tr>
<% items.forEach(function(item) { %>
<tr>
<td><%= item.name %></td>
<td><%= item.quantity %></td>
<td>$<%= item.price %></td>
</tr>
<% }); %>
</table>
<p><strong>Total: $<%= total %></strong></p>
</body>
</html>
Pug template (views/emails/notification.pug):
doctype html
html
head
title= title
body
h1= message
p Dear #{username},
p This is a notification about your account activity.
if urgent
p.alert This requires immediate attention!
ul
each item in items
li= item
p Sent at: #{timestamp}Mailable Class Patterns
// Simple mailable with inline HTML
class SimpleEmail extends Mailable {
build() {
return this.subject('Hello').html('<h1>Hello!</h1>');
}
}
// Mailable with template
class TemplateEmail extends Mailable {
constructor(private data: Record<string, unknown>) { super(); }
build() {
return this.subject('Welcome').view('welcome', this.data);
}
}
// Mailable with all options
class FullEmail extends Mailable {
build() {
return this
.from('sender@example.com')
.subject('Full Example')
.html('<h1>Hello!</h1>')
.text('Hello!')
.cc('cc@example.com')
.bcc('bcc@example.com')
.replyTo('reply@example.com')
.attach('./file.pdf', 'document.pdf')
.withHeaders({ 'X-Custom': 'value' });
}
}Markdown Mail with Components
class NewsletterEmail extends MarkdownMailable {
constructor(private articles: { title: string; url: string }[]) {
super();
}
build(): this {
const articleList = this.articles
.map(a => `- [${a.title}](${a.url})`)
.join('\n');
return this
.subject('Weekly Newsletter')
.from('newsletter@example.com')
.markdown(`# Weekly Newsletter
Here are this week's top articles:
${articleList}
[button url="https://example.com/archive" color="primary"]View All Articles[/button]
[panel]
You're receiving this because you subscribed to our newsletter.
[Unsubscribe](https://example.com/unsubscribe)
[/panel]
[table]
| Category | Count |
|----------|-------|
| Tech | 5 |
| Science | 3 |
[/table]`);
}
}Queue Patterns
// Immediate queue
await Mail.to('user@example.com').queue(new WelcomeEmail(user));
// Delayed (send in 5 minutes)
await Mail.to('user@example.com').later(300, new FollowUpEmail(user));
// Scheduled (send at specific time)
const sendAt = new Date('2026-12-25T09:00:00');
await Mail.to('user@example.com').at(sendAt, new ChristmasEmail());
// Via facade
await Mail.queue(new WelcomeEmail(user));
await Mail.later(new FollowUpEmail(user), 300);
await Mail.at(new ChristmasEmail(), sendAt);
// Process queued emails (worker)
await Mail.processQueue();Failover Configuration
Mail.configure({
default: 'smtp',
from: { address: 'noreply@example.com', name: 'My App' },
mailers: {
smtp: { driver: 'smtp', host: 'smtp.example.com', port: 587, auth: { user: 'u', pass: 'p' } },
sendgrid: { driver: 'sendgrid', apiKey: 'SG.xxx' },
ses: { driver: 'ses', region: 'us-east-1' },
},
failover: {
chain: ['sendgrid', 'ses'],
maxRetriesPerProvider: 2,
retryDelay: 1000,
failoverDelay: 500,
onFailover: (event) => {
console.log(`[Failover] ${event.failedMailer} failed → trying ${event.nextMailer}`);
},
},
});Testing Workflow
import { Mail, Mailable, MarkdownMailable } from '@impruthvi/nodemail';
class OrderEmail extends Mailable {
constructor(private orderId: string) { super(); }
build() {
return this.subject(`Order #${this.orderId}`).html(`<p>Order confirmed</p>`);
}
}
describe('Order System', () => {
beforeEach(() => Mail.fake());
afterEach(() => Mail.restore());
it('sends order confirmation', async () => {
await Mail.to('customer@example.com').send(new OrderEmail('123'));
Mail.assertSent(OrderEmail);
Mail.assertSent(OrderEmail, (m) =>
m.hasTo('customer@example.com') &&
m.hasSubject('Order #123') &&
m.htmlContains('Order confirmed')
);
});
it('queues order for background sending', async () => {
await Mail.to('customer@example.com').queue(new OrderEmail('456'));
Mail.assertQueued(OrderEmail);
Mail.assertNothingSent();
});
it('handles no emails sent', async () => {
Mail.assertNothingSent();
Mail.assertNothingQueued();
});
});