A flexible email utility that supports both Mailgun and self-hosted SMTP servers with automatic transport management, rate limiting, and retry capabilities. Perfect for applications that need to send emails through different providers.
- đź“§ Supports both Mailgun and self-hosted SMTP
- 🔄 Automatic transport pooling and management
- 🚀 Connection reuse and optimization
- ⏱️ Automatic cleanup of idle connections
- 🛡️ Rate limiting for authenticated SMTP connections
- đź§Ş Development mode support
- ♻️ Automatic retries on failed email sends
npm install @jvp/dual-mailer
import { DualMailer } from '@jvp/dual-mailer';
const mailer = new DualMailer({
host: 'localhost',
port: 1025,
is_dev: true
});
const mailer = new DualMailer({
host: 'smtp.your-domain.com',
port: 587,
user: 'your-username',
password: 'your-password',
is_dev: process.env.NODE_ENV === 'development'
});
const mailer = new DualMailer({
mailgun_api_key: 'your-mailgun-key',
mailgun_domain: 'your-domain.com',
is_dev: process.env.NODE_ENV === 'development'
});
await mailer.send_mail({
to: '[email protected]',
subject: 'Hello World',
html: {
title: 'My Email',
body: '<h1>Hello World</h1>'
}
});
To enable retries for email sends, you can pass a retry
option when creating the DualMailer
instance:
const mailer = new DualMailer(config, {
retry: {
max_retries: 3,
retry_delay: 1000,
retryable_errors: ['Temporary Error']
}
});
The retry
option accepts the following properties:
Option | Type | Default | Description |
---|---|---|---|
max_retries |
number | 3 | The maximum number of retry attempts |
retry_delay |
number | 1000 | The initial delay between retries in milliseconds (the delay doubles for each subsequent retry) |
retryable_errors |
string[] | [] |
A list of error messages that should trigger a retry (if empty, all errors will be retried) |
By default, retries are disabled. To enable them, you need to provide the retry
option when creating the DualMailer
instance.
await mailer.send_mail({
to: '[email protected]',
subject: 'Styled Email',
from: '[email protected]', // Optional
reply_to: '[email protected]', // Optional
html: {
title: 'Styled Email',
style: `
.header { color: blue; }
.content { padding: 20px; }
`,
body: `
<div class="header">
<h1>Welcome!</h1>
</div>
<div class="content">
<p>This is a styled email.</p>
</div>
`
}
});
Dual Mailer supports rate limiting for authenticated SMTP connections. You can configure the rate limiting options when creating the DualMailer
instance:
const mailer = new DualMailer(config, {
rate_limit: {
max_connections: 5,
max_messages_per_connection: 200,
rate_delta: 1000,
rate_limit: 5
}
});
The rate_limit
option accepts the following properties:
Option | Type | Default | Description |
---|---|---|---|
max_connections |
number | 5 | The maximum number of concurrent SMTP connections |
max_messages_per_connection |
number | 200 | The maximum number of messages per SMTP connection |
rate_delta |
number | 1000 | The time window for rate limiting in milliseconds |
rate_limit |
number | 5 | The maximum number of messages to send within the rate delta |
Rate limiting is only applied to authenticated SMTP connections. For Mailgun, rate limiting is handled by the Mailgun service.
Make sure to clean up when shutting down your application:
await mailer.destroy();
Option | Type | Required | Description |
---|---|---|---|
host |
string | No* | SMTP host |
port |
number | No* | SMTP port |
user |
string | No | SMTP username for authentication |
password |
string | No** | SMTP password for authentication |
mailgun_api_key |
string | No*** | Mailgun API key |
mailgun_domain |
string | No*** | Mailgun domain |
noreply_email |
string | No | Default from address |
is_dev |
boolean | No | Development mode flag |
* Required if using SMTP transport (must provide both host and port)
** Required if SMTP user is provided
*** Required if using Mailgun transport (must provide both api_key and domain)
You must provide either:
- SMTP configuration (host + port), or
- Mailgun configuration (api_key + domain)
Option | Type | Required | Description |
---|---|---|---|
to |
string | Yes | Recipient email address |
subject |
string | Yes | Email subject |
text |
string | No | Plain text version |
from |
string | No | Sender address |
html |
object | Yes | HTML email content |
reply_to |
string | No | Reply-to address |
Option | Type | Required | Description |
---|---|---|---|
title |
string | Yes | Email title |
style |
string | No | CSS styles |
body |
string | Yes | HTML content |
The mailer accepts an optional logger in its constructor options. You can provide your own logging implementation or disable logging entirely.
// With Winston
import winston from 'winston';
const logger = winston.createLogger({
transports: [new winston.transports.Console()]
});
const mailer = new DualMailer(config, {
logger: (level, message, meta) => logger.log(level, message, meta)
});
// With Pino
import pino from 'pino';
const logger = pino();
const mailer = new DualMailer(config, {
logger: (level, message, meta) => logger[level]({ msg: message, ...meta })
});
// With custom logging service
const mailer = new DualMailer(config, {
logger: (level, message, meta) => {
MyLoggingService.log({
severity: level,
message,
timestamp: new Date(),
...meta
});
}
});
const mailer = new DualMailer(config, { silent: true });
The logger function receives:
level
: 'info' | 'warn' | 'error'message
: String description of the eventmeta
: Object with additional context (timestamps, email details, errors)
When is_dev
is true:
- SSL is disabled for SMTP
- Failed emails log their content instead of throwing
- Transport cleanup is disabled
try {
await mailer.send_mail({
to: '[email protected]',
subject: 'Test Email',
html: {
title: 'Test',
body: '<h1>Hello</h1>'
}
});
} catch (error) {
console.error('Failed to send email:', error);
}
- Always call
destroy()
when shutting down your application - Use environment variables for sensitive configuration
- Implement proper error handling
- Set appropriate timeouts for your use case
- For local development, use tools like Mailhog with simple SMTP configuration
- Use authenticated SMTP or Mailgun for production environments
- Configure appropriate rate limiting settings for your use case
MIT
Contributions are welcome! Please feel free to submit a Pull Request.