@theloremi/mailer
v0.1.0
Published
Zero-config email sending SDK. SMTP passthrough + optional relay.
Maintainers
Readme
@theloremi/mailer
Zero-config email sending SDK. SMTP passthrough + optional relay.
Install
npm install @theloremi/mailerQuick Start
import { Mailer } from '@theloremi/mailer'
const mailer = new Mailer({ host: 'smtp.gmail.com', port: 465, user: '[email protected]', pass: 'app-password' })
await mailer.send({ to: '[email protected]', from: '[email protected]', subject: 'Hello', html: '<p>Hello world</p>' })API Reference
new Mailer(config)
Create a mailer instance. Supports two modes:
SMTP mode — connect directly to any SMTP server:
const mailer = new Mailer({
host: 'smtp.gmail.com',
port: 465,
secure: true,
user: '[email protected]',
pass: 'app-password',
})Relay mode — send through a Loremi relay Worker:
const mailer = new Mailer({
apiKey: 'your-api-key',
relay: 'https://mailer.theloremi.com',
}).send(message)
Send a single email. Returns Promise<SendResult>.
const result = await mailer.send({
to: '[email protected]',
from: '[email protected]',
subject: 'Welcome',
html: '<h1>Welcome aboard</h1><p>Thanks for signing up.</p>',
text: 'Welcome aboard. Thanks for signing up.',
replyTo: '[email protected]',
cc: ['[email protected]'],
bcc: '[email protected]',
attachments: [
{ filename: 'guide.pdf', path: './guide.pdf' },
],
})
console.log(result) // { ok: true, messageId: '<abc@smtp>' }.sendBatch(messages)
Send multiple emails. In relay mode, uses the native batch endpoint. In SMTP mode, sends sequentially. Returns Promise<BatchResult>.
const batch = await mailer.sendBatch([
{ to: '[email protected]', from: '[email protected]', subject: 'Hi Alice', html: '<p>Hi Alice</p>' },
{ to: '[email protected]', from: '[email protected]', subject: 'Hi Bob', html: '<p>Hi Bob</p>' },
])
console.log(batch) // { sent: 2, failed: 0, results: [...] }.registerTemplate(name, fn)
Register a reusable email template. The template function receives data and returns { subject, html, text? }.
mailer.registerTemplate<{ name: string; code: string }>('welcome', (data) => ({
subject: `Welcome, ${data.name}!`,
html: `<h1>Welcome, ${data.name}</h1><p>Your code: <strong>${data.code}</strong></p>`,
text: `Welcome, ${data.name}. Your code: ${data.code}`,
})).sendTemplate(name, message)
Send an email using a registered template. Returns Promise<SendResult>.
const result = await mailer.sendTemplate('welcome', {
to: '[email protected]',
from: '[email protected]',
data: { name: 'Alice', code: 'ABC123' },
}).sendTemplateBatch(name, messages)
Send multiple emails using a registered template. Returns Promise<BatchResult>.
const batch = await mailer.sendTemplateBatch('welcome', [
{ to: '[email protected]', from: '[email protected]', data: { name: 'Alice', code: 'ABC123' } },
{ to: '[email protected]', from: '[email protected]', data: { name: 'Bob', code: 'XYZ789' } },
])SMTP Providers
Gmail
new Mailer({ host: 'smtp.gmail.com', port: 465, user: '[email protected]', pass: 'app-password' })Outlook / Microsoft 365
new Mailer({ host: 'smtp.office365.com', port: 587, secure: false, user: '[email protected]', pass: 'password' })Zoho
new Mailer({ host: 'smtp.zoho.com', port: 465, user: '[email protected]', pass: 'password' })Custom SMTP
new Mailer({ host: 'mail.example.com', port: 465, secure: true, user: 'user', pass: 'pass' })Relay Mode
The Loremi relay is a Cloudflare Worker that receives email requests over HTTP and forwards them via SMTP. Useful when your environment cannot make direct SMTP connections (edge functions, serverless, browsers via API).
const mailer = new Mailer({
apiKey: process.env.MAILER_API_KEY,
relay: 'https://mailer.theloremi.com',
})When both apiKey and relay are provided, the SDK uses relay mode. All .send() and .sendBatch() calls are forwarded as HTTP POST requests with Bearer authentication.
See relay/README.md for self-hosting instructions.
Templates
Templates are plain functions. No templating engine required.
import { Mailer } from '@theloremi/mailer'
const mailer = new Mailer({ host: 'smtp.gmail.com', port: 465, user: '[email protected]', pass: 'app-password' })
interface InvoiceData {
customerName: string
amount: number
invoiceUrl: string
}
mailer.registerTemplate<InvoiceData>('invoice', (data) => ({
subject: `Invoice for ${data.customerName} — $${data.amount.toFixed(2)}`,
html: `
<div style="font-family: sans-serif; max-width: 600px; margin: 0 auto;">
<h1>Invoice</h1>
<p>Hi ${data.customerName},</p>
<p>Your invoice for <strong>$${data.amount.toFixed(2)}</strong> is ready.</p>
<a href="${data.invoiceUrl}" style="display: inline-block; padding: 12px 24px; background: #EC572D; color: #fff; text-decoration: none; border-radius: 4px;">
View Invoice
</a>
</div>
`,
text: `Hi ${data.customerName}, your invoice for $${data.amount.toFixed(2)} is ready: ${data.invoiceUrl}`,
}))
await mailer.sendTemplate('invoice', {
to: '[email protected]',
from: '[email protected]',
data: { customerName: 'Alice', amount: 99.0, invoiceUrl: 'https://example.com/invoice/123' },
})You can also use the TemplateRegistry class standalone:
import { TemplateRegistry } from '@theloremi/mailer'
const registry = new TemplateRegistry()
registry.register('welcome', (data) => ({ subject: `Hi ${data.name}`, html: `<p>Hi ${data.name}</p>` }))Attachments
Buffer content:
await mailer.send({
to: '[email protected]',
from: '[email protected]',
subject: 'Report attached',
html: '<p>See attached.</p>',
attachments: [
{ filename: 'report.csv', content: Buffer.from('name,email\nAlice,[email protected]'), contentType: 'text/csv' },
],
})File path:
await mailer.send({
to: '[email protected]',
from: '[email protected]',
subject: 'Report attached',
html: '<p>See attached.</p>',
attachments: [
{ filename: 'report.pdf', path: './reports/monthly.pdf' },
],
})Types
All types are exported from the package:
import type {
MailerConfig,
EmailMessage,
Attachment,
SendResult,
BatchResult,
TemplateFunction,
TemplateMessage,
} from '@theloremi/mailer'| Type | Description |
|------|-------------|
| MailerConfig | Constructor config — SMTP fields or apiKey + relay |
| EmailMessage | Full email payload: to, from, subject, html, text?, replyTo?, cc?, bcc?, attachments? |
| Attachment | File attachment: filename, content?, path?, contentType? |
| SendResult | { ok: boolean, messageId?: string, error?: string } |
| BatchResult | { sent: number, failed: number, results: (SendResult & { to: string })[] } |
| TemplateFunction<T> | (data: T) => { subject: string, html: string, text?: string } |
| TemplateMessage<T> | Template send payload: to, from, data, replyTo?, cc?, bcc?, attachments? |
Self-Hosting the Relay
The relay Worker lives in the relay/ directory. It is a Cloudflare Worker built with Hono that receives HTTP email requests and forwards them via nodemailer SMTP.
- Clone the repo and
cd relay - Set secrets:
wrangler secret put SMTP_HOST,SMTP_PORT,SMTP_USER,SMTP_PASS,API_KEY - Deploy:
wrangler deploy
Full instructions in relay/README.md.
Ecosystem
@theloremi/mailer is part of The Loremi open-source ecosystem.
| Package | Description |
|---------|-------------|
| @theloremi/mailer | Email sending SDK (this package) |
| @theloremi/cms-client | Headless CMS client |
| @theloremi/cleepa | URL shortener and link analytics |
License
MIT © 2026 The Loremi Ltd
