Skip to content

πŸ“« Lightweight, zero-dependency TypeScript email library with pluggable providers and MailCrab support for local development.

License

Notifications You must be signed in to change notification settings

productdevbook/unemail

Repository files navigation

unemail

npm version npm downloads bundle JSDocs License

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 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

# Using pnpm
pnpm add unemail

# Using npm
npm install unemail

# Using yarn
yarn add unemail

Quick Start

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: '<p>This is a test email sent using <strong>unemail</strong> library</p>'
})

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

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

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)

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

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: '<p>HTML content</p>'
})

// 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: '<p>HTML content</p>',

  // 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: '<previous-message-id@example.com>',
  references: ['<ref1@example.com>', '<ref2@example.com>'],

  // 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

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:

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: '<p>HTML version of the email</p>',

  // 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:

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<Result<EmailResult>> {
      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:

interface Result<T = any> {
  success: boolean
  data?: T
  error?: Error
}

This allows for consistent error handling:

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

# 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:

# 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:

# 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, which uses a similar approach for storage drivers.

License

Published under the MIT license. Made by @productdevbook and community πŸ’›

About

πŸ“« Lightweight, zero-dependency TypeScript email library with pluggable providers and MailCrab support for local development.

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

 

Packages

No packages published

Contributors 3

  •  
  •  
  •  
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy