@workermailer/smtp • v0.1.4

SMTP library

Direct SMTP delivery from Cloudflare Workers.

@workermailer/smtp keeps SMTP inside the Worker runtime with TCP sockets, DSN support, lifecycle hooks, attachments, and queue helpers.

Highlights

Built for SMTP on the edge.

  • Cloudflare TCP sockets with SMTP auth modes.
  • DSN, hooks, attachments, inline CID assets, and explicit errors.
  • Queue helpers for async delivery off the request path.
terminal Install with Bun
bun add @workermailer/smtp
send-email.ts Quick start
import { WorkerMailer } from '@workermailer/smtp'

const mailer = await WorkerMailer.connect({
  host: 'smtp.example.com',
  port: 465,
  secure: true,
  authType: 'plain',
  credentials: {
    username: env.SMTP_USERNAME,
    password: env.SMTP_PASSWORD
  }
})

await mailer.send({
  from: { name: 'Worker Mailer', email: 'noreply@example.com' },
  to: { name: 'Alice', email: 'alice@example.com' },
  subject: 'Hello from the edge',
  text: 'This message was sent from a Cloudflare Worker.',
  html: '<h1>Hello from the edge</h1><p>SMTP over TCP sockets.</p>'
})

Queues

Queue helpers for bursty traffic.

Move email processing into Cloudflare Queues when you need retries, backpressure, or batching across requests.

queue.ts Queue handler
import {
  createQueueHandler,
  enqueueEmail,
  type QueueEmailMessage
} from '@workermailer/smtp/queue'

interface Env {
  EMAIL_QUEUE: Queue<QueueEmailMessage>
}

export default {
  async fetch(_request: Request, env: Env) {
    await enqueueEmail(env.EMAIL_QUEUE, {
      mailerOptions: {
        host: 'smtp.example.com',
        port: 465,
        secure: true,
        credentials: {
          username: env.SMTP_USERNAME,
          password: env.SMTP_PASSWORD
        }
      },
      emailOptions: {
        from: 'noreply@example.com',
        to: 'alice@example.com',
        subject: 'Queued email',
        text: 'Sent from Cloudflare Queues.'
      }
    })

    return new Response('queued')
  },

  async queue(batch: MessageBatch<QueueEmailMessage>) {
    const handleQueue = createQueueHandler()
    await handleQueue(batch)
  }
}

Need Resend instead?

Use the Resend library for API-based delivery with the same queue patterns.