Transactional email. One send().
createMailer() gives you a single Mailer over swappable transports — an
in-memory capture for dev and tests, an HTTP transport for any JSON API, or SMTP. Resend and
Postmark ship as one-line presets, and renderTemplate covers HTML-escaped
interpolation without a dependency.
Send a message #
createMailer() defaults to the memory transport — it captures every message on
mailer.sent instead of sending, ideal for dev and assertions. send()
normalizes to/cc/bcc (single or array) to string arrays and
returns { accepted, id }.
import { createMailer } from '@apex-stack/core/server'
const mailer = createMailer() // memory transport (default)
const result = await mailer.send({
to: 'to@example.com',
from: 'from@example.com',
subject: 'Hello',
html: '<b>hi</b>',
text: 'hi',
})
mailer.sent[0] // the normalized message (memory transport)
result.accepted // → ['to@example.com']
Resend & Postmark #
Pass a preset's config straight into createMailer. Each preset wires the right
endpoint, auth header, and body shape; result.id comes back as the provider's message
id.
import { createMailer, resend, postmark } from '@apex-stack/core/server'
// Resend — POST https://api.resend.com/emails, Bearer auth
const mailer = createMailer(resend(process.env.RESEND_API_KEY))
// Postmark — POST https://api.postmarkapp.com/email, X-Postmark-Server-Token
const pm = createMailer(postmark(process.env.POSTMARK_TOKEN))
await mailer.send({ to: 'to@example.com', from: 'from@example.com', subject: 'Hi', html: '<p>x</p>' })
HTTP & SMTP #
For any other provider, the HTTP transport POSTs JSON to your endpoint with custom headers; a
non-2xx response rejects with the status and body. An smtp transport is available for
classic mail servers.
const mailer = createMailer({
driver: 'http',
endpoint: 'https://mail.internal/send',
headers: { authorization: 'Bearer key' },
})
// Classic mail server
const smtp = createMailer({ driver: 'smtp', /* host, port, auth, … */ })
Templates #
renderTemplate substitutes {{ var }} and HTML-escapes the value by
default; use {{{ var }}} for raw, unescaped output. Numbers and booleans render;
missing or nullish values become empty strings.
import { renderTemplate } from '@apex-stack/core/server'
renderTemplate('<p>Hi {{ name }}</p>', { name: '<script>' })
// → '<p>Hi <script></p>' — escaped by default
renderTemplate('{{{ badge }}} — {{ label }}', { badge: '<span>NEW</span>', label: '<b>' })
// → '<span>NEW</span> — <b>' — triple-brace is raw
Pair mail with Queues to send off the request path, or with Notifications to make it one channel among several.