https://github.com/productdevbook/unemail
๐ซ Lightweight, zero-dependency TypeScript email library with pluggable providers and MailCrab support for local development.
https://github.com/productdevbook/unemail
email unpkg unplugin
Last synced: 12 days ago
JSON representation
๐ซ Lightweight, zero-dependency TypeScript email library with pluggable providers and MailCrab support for local development.
- Host: GitHub
- URL: https://github.com/productdevbook/unemail
- Owner: productdevbook
- License: mit
- Created: 2025-04-05T08:12:35.000Z (9 months ago)
- Default Branch: main
- Last Pushed: 2025-04-17T18:51:00.000Z (9 months ago)
- Last Synced: 2025-04-18T08:09:12.548Z (9 months ago)
- Topics: email, unpkg, unplugin
- Language: TypeScript
- Homepage:
- Size: 239 KB
- Stars: 74
- Watchers: 2
- Forks: 1
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# unemail
[![npm version][npm-version-src]][npm-version-href]
[![npm downloads][npm-downloads-src]][npm-downloads-href]
[![bundle][bundle-src]][bundle-href]
[![JSDocs][jsdocs-src]][jsdocs-href]
[![License][license-src]][license-href]
A modern, TypeScript-first email sending library with support for multiple providers and ESM-only architecture.
## Supported Email Services
- **SMTP** - Any standard SMTP server including Gmail, Outlook, Office 365, etc.
- **AWS SES** - Amazon Simple Email Service
- **Resend** - Modern email API for developers
- **HTTP API** - Custom HTTP API endpoints for email delivery
- **MailCrab** - Local development email testing (via SMTP provider)
> ๐ข **Want to add a provider?** We welcome pull requests for new providers! See the [Creating Custom Email Providers](#creating-custom-email-providers) section for guidance.
## Features
- ๐ฆ **Multiple Providers** - Support for various email services including AWS SES, MailCrab, HTTP APIs, and more
- ๐ **Provider Pattern** - Easily extend with custom providers
- ๐ **Type-safe** - Full TypeScript support with generics and strict typing
- ๐ค **ESM Only** - Modern ES modules architecture
- ๐งช **Well-tested** - Comprehensive test suite for all components
- ๐ **Zero External Dependencies** - Core functionality has no runtime dependencies
- ๐ **Error Handling** - Consistent error handling across all providers
## Installation
```bash
# Using pnpm
pnpm add unemail
# Using npm
npm install unemail
# Using yarn
yarn add unemail
```
## Quick Start
```typescript
import { createEmailService } from 'unemail'
import httpProvider from 'unemail/providers/http'
// Create a service with your preferred provider
const emailService = createEmailService({
provider: httpProvider({
endpoint: 'https://api.example.com/email',
apiKey: 'your-api-key'
})
})
// Send an email
const result = await emailService.sendEmail({
from: { email: 'sender@example.com', name: 'Sender Name' },
to: { email: 'recipient@example.com', name: 'Recipient Name' },
subject: 'Hello from unemail',
text: 'This is a test email sent using unemail library',
html: '
This is a test email sent using unemail library
'
})
if (result.success) {
console.log('Email sent successfully!', result.data.messageId)
}
else {
console.error('Failed to send email:', result.error)
}
```
## Available Providers
### AWS SES Provider
```typescript
import { createEmailService } from 'unemail'
import awsSesProvider from 'unemail/providers/aws-ses'
const emailService = createEmailService({
provider: awsSesProvider({
accessKeyId: 'AWS_ACCESS_KEY',
secretAccessKey: 'AWS_SECRET_KEY',
region: 'us-east-1'
})
})
```
### HTTP Provider
```typescript
import { createEmailService } from 'unemail'
import httpProvider from 'unemail/providers/http'
const emailService = createEmailService({
provider: httpProvider({
endpoint: 'https://api.yourservice.com/send',
apiKey: 'your-api-key',
method: 'POST', // optional, defaults to POST
headers: { // optional, additional headers
'X-Custom-Header': 'custom-value'
}
})
})
```
### MailCrab Provider (for development)
```typescript
import { createEmailService } from 'unemail'
import smtpProvider from 'unemail/providers/smtp'
const emailService = createEmailService({
provider: smtpProvider({
host: 'localhost',
port: 1025, // default MailCrab port
secure: false // typically false for development
})
})
```
### SMTP Provider
```typescript
import { createEmailService } from 'unemail'
import smtpProvider from 'unemail/providers/smtp'
// Basic configuration
const emailService = createEmailService({
provider: smtpProvider({
host: 'smtp.example.com',
port: 587,
secure: false, // use TLS
user: 'username',
password: 'password'
})
})
// Advanced configuration with enhanced security and features
const advancedEmailService = createEmailService({
provider: smtpProvider({
host: 'smtp.example.com',
port: 587,
secure: false,
user: 'username',
password: 'password',
// TLS options
rejectUnauthorized: true, // Verify SSL certificates (set to false to ignore certificate errors)
// Connection pooling for sending multiple emails efficiently
pool: true,
maxConnections: 5,
// Enhanced authentication
authMethod: 'CRAM-MD5', // 'LOGIN', 'PLAIN', 'CRAM-MD5', or 'OAUTH2'
// OAuth2 authentication (if using OAUTH2 authMethod)
oauth2: {
user: 'user@example.com',
clientId: 'client-id',
clientSecret: 'client-secret',
refreshToken: 'refresh-token',
accessToken: 'access-token',
expires: 1714939200000
},
// DKIM signing to improve deliverability
dkim: {
domainName: 'example.com',
keySelector: 'mail',
privateKey: '-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----'
}
})
})
// Basic email sending
await emailService.sendEmail({
from: { email: 'sender@example.com', name: 'Sender' },
to: { email: 'recipient@example.com' },
subject: 'Test email',
text: 'Plain text content',
html: '
HTML content
'
})
// Advanced email sending with SMTP-specific options
await advancedEmailService.sendEmail({
from: { email: 'sender@example.com', name: 'Sender' },
to: { email: 'recipient@example.com' },
subject: 'Test email',
text: 'Plain text content',
html: '
HTML content
',
// Basic SMTP-specific options
priority: 'high', // 'high', 'normal', or 'low'
dsn: {
success: true, // Request successful delivery notification
failure: true, // Request failure notification
delay: true // Request delay notification
},
// Threading and references
inReplyTo: '',
references: ['', ''],
// Email management
listUnsubscribe: 'mailto:unsubscribe@example.com',
// Gmail-specific features
googleMailHeaders: {
promotionalContent: true, // Mark as promotional content
feedbackId: 'campaign:12345:user:123', // For engagement tracking
category: 'promotions' // 'primary', 'social', 'promotions', 'updates', or 'forums'
},
// Control DKIM signing per email
useDkim: true
})
```
### Resend Provider
```typescript
import { createEmailService } from 'unemail'
import resendProvider from 'unemail/providers/resend'
const emailService = createEmailService({
provider: resendProvider({
apiKey: 'your-resend-api-key'
})
})
```
## Email Options
Send emails with a variety of options:
```typescript
import { Buffer } from 'node:buffer'
import fs from 'node:fs'
const result = await emailService.sendEmail({
// Required fields
from: { email: 'sender@example.com', name: 'Sender Name' },
to: [
{ email: 'recipient1@example.com', name: 'Recipient One' },
{ email: 'recipient2@example.com', name: 'Recipient Two' }
],
subject: 'Test Email with Attachments',
// Content - at least one of text or html is required
text: 'Plain text version of the email',
html: '
HTML version of the email
',
// Optional fields
cc: { email: 'cc@example.com', name: 'CC Recipient' },
bcc: { email: 'bcc@example.com', name: 'BCC Recipient' },
// Custom headers
headers: {
'X-Custom-Header': 'custom-value'
},
// Attachments
attachments: [
{
filename: 'document.pdf',
content: Buffer.from('...'), // Can be Buffer or Base64 string
contentType: 'application/pdf'
},
{
filename: 'image.png',
content: fs.readFileSync('path/to/image.png'),
contentType: 'image/png'
}
],
// Reply-to address
replyTo: { email: 'reply@example.com', name: 'Reply Handler' }
})
```
## Creating Custom Email Providers
You can easily create custom providers for any email service:
```typescript
import type { EmailOptions, EmailResult, Result } from 'unemail/types'
import { createEmailService, defineProvider } from 'unemail'
// Define your provider
const myCustomProvider = defineProvider((options = {}) => {
// Provider initialization
const apiKey = options.apiKey
const apiUrl = options.apiUrl || 'https://api.default-service.com'
// Method implementations
return {
name: 'my-custom-provider',
features: {
attachments: true,
html: true,
templates: false,
tracking: false
},
options,
async initialize() {
// Initialize your provider if needed
// e.g. validate credentials, set up connections, etc.
},
async isAvailable() {
// Check if the provider is available
// e.g. test connection, validate credentials, etc.
return true
},
async sendEmail(options: EmailOptions): Promise> {
try {
// Implementation of email sending logic
// On success
return {
success: true,
data: {
messageId: 'generated-or-returned-message-id',
sent: true,
timestamp: new Date(),
provider: 'my-custom-provider'
}
}
}
catch (error) {
// On error
return {
success: false,
error: error as Error
}
}
}
}
})
const emailService = createEmailService({
provider: myCustomProvider({
apiKey: 'your-api-key',
apiUrl: 'https://api.your-service.com'
})
})
```
## Error Handling
All provider methods return a standardized `Result` type:
```typescript
interface Result {
success: boolean
data?: T
error?: Error
}
```
This allows for consistent error handling:
```typescript
const result = await emailService.sendEmail({
// email options...
})
if (result.success) {
// Handle success
console.log(`Email sent with ID: ${result.data.messageId}`)
}
else {
// Handle error
console.error(`Failed to send email: ${result.error.message}`)
}
```
## Development Setup
### Prerequisites
- Node.js 20.11.1 or higher
- pnpm
### Local Development
```bash
# Clone the repository
git clone https://github.com/your-username/unemail.git
cd unemail
# Install dependencies
pnpm install
# Build the package
pnpm build
# Run tests
pnpm test
```
### Testing with MailCrab
For local development, you can use MailCrab, a local SMTP server:
```bash
# Run the MailCrab setup script
pnpm mailcrab
# Test with the example script
pnpm example
```
#### unemail-mailcrab CLI
The package includes a CLI tool called `unemail-mailcrab` that helps you set up and manage a MailCrab container for local email testing:
```bash
# Install globally to use the CLI from anywhere
npm install -g unemail
# Run the CLI
unemail-mailcrab
```
The CLI tool:
- Checks if Docker is installed
- Verifies if ports 1025 (SMTP) and 1080 (Web UI) are available
- Pulls the MailCrab Docker image if not already available
- Manages existing MailCrab containers (start/stop/create new)
- Sets up a Docker container running MailCrab
- Provides detailed instructions for using MailCrab with unemail
After running the CLI, you can:
- Send emails to localhost:1025 using the MailCrab provider
- View all sent emails in the MailCrab web UI at http://localhost:1080
- Stop/start the container with `docker stop unemail-mailcrab` and `docker start unemail-mailcrab`
## Credits
This project's architecture and provider pattern was inspired by [unjs/unstorage](https://github.com/unjs/unstorage), which uses a similar approach for storage drivers.
## License
Published under the [MIT](https://github.com/productdevbook/unemail/blob/main/LICENSE) license.
Made by [@productdevbook](https://github.com/productdevbook) and [community](https://github.com/productdevbook/unemail/graphs/contributors) ๐
[npm-version-src]: https://img.shields.io/npm/v/unemail?style=flat&colorA=080f12&colorB=1fa669
[npm-version-href]: https://npmjs.com/package/unemail
[npm-downloads-src]: https://img.shields.io/npm/dm/unemail?style=flat&colorA=080f12&colorB=1fa669
[npm-downloads-href]: https://npmjs.com/package/unemail
[bundle-src]: https://deno.bundlejs.com/badge?q=unemail@0.0.4
[bundle-href]: https://deno.bundlejs.com/badge?q=unemail@0.0.4
[license-src]: https://img.shields.io/github/license/productdevbook/unemail.svg?style=flat&colorA=080f12&colorB=1fa669
[license-href]: https://github.com/productdevbook/unemail/blob/main/LICENSE
[jsdocs-src]: https://img.shields.io/badge/jsdocs-reference-080f12?style=flat&colorA=080f12&colorB=1fa669
[jsdocs-href]: https://www.jsdocs.io/package/unemail