@jvp/dual-mailer
v0.6.2
Published
Dual mailer package
Readme
Dual Mailer
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.
Features
- 📧 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
Installation
npm install @jvp/dual-mailerUsage
Basic SMTP Setup (e.g., for Mailhog)
import { DualMailer } from '@jvp/dual-mailer';
const mailer = new DualMailer({
host: 'localhost',
port: 1025,
is_dev: true
});Authenticated SMTP Setup
const mailer = new DualMailer({
host: 'smtp.your-domain.com',
port: 587,
user: 'your-username',
password: 'your-password',
is_dev: process.env.NODE_ENV === 'development'
});Mailgun Setup
const mailer = new DualMailer({
mailgun_api_key: 'your-mailgun-key',
mailgun_domain: 'your-domain.com',
is_dev: process.env.NODE_ENV === 'development'
});Sending Emails
await mailer.send_mail({
to: '[email protected]',
subject: 'Hello World',
html: {
title: 'My Email',
body: '<h1>Hello World</h1>'
}
});Enabling Retries
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.
Advanced HTML Emails
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>
`
}
});Rate Limiting
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.
Cleanup
Make sure to clean up when shutting down your application:
await mailer.destroy();Configuration Options
| 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)
Email Options
| 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 |
HTML Object Options
| Option | Type | Required | Description |
|--------|------|----------|-------------|
| title | string | Yes | Email title |
| style | string | No | CSS styles |
| body | string | Yes | HTML content |
Logging Options
The mailer accepts an optional logger in its constructor options. You can provide your own logging implementation or disable logging entirely.
Using Custom Logger
// 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
});
}
});Disabling Logging
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)
Development Mode
When is_dev is true:
- SSL is disabled for SMTP
- Failed emails log their content instead of throwing
- Transport cleanup is disabled
Error Handling
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);
}Best Practices
- 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
License
MIT
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
