Mail

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 &lt;script&gt;</p>'  — escaped by default

renderTemplate('{{{ badge }}} — {{ label }}', { badge: '<span>NEW</span>', label: '<b>' })
// → '<span>NEW</span> — &lt;b&gt;'  — triple-brace is raw

Pair mail with Queues to send off the request path, or with Notifications to make it one channel among several.