Migrate from Nodemailer
Move SMTP configuration and message fields to sently incrementally.
Keep your welcome email, receipt, or password-reset fields (from, to, subject, text / html).
Replace createTransport with createSMTPMailer, rename sendMail to send, and await the factory before the first send.
The one rule
Use createSMTPMailer for relay host/port/auth. Use createMailer only when you already have an explicit transport.
Quick start
import { createSMTPMailer } from "sently/smtp";
const mailer = await createSMTPMailer({
host: "smtp.example.com",
port: 587,
auth: { user: "user@example.com", pass: process.env.SMTP_PASSWORD! },
});- const info = await transporter.sendMail({
+ const result = await mailer.send({
from: "Acme <hello@example.com>",
to: "person@example.com",
subject: "Welcome",
text: "Thanks for joining.",
});console.log(result.messageId, result.accepted, result.rejected);API map
| Nodemailer | sently |
|---|---|
createTransport({ host, port, auth }) | await createSMTPMailer({ host, port, auth }) |
transporter.sendMail(msg) | mailer.send(msg) |
transporter.verify() | mailer.verify() |
transporter.close() | mailer.close() |
info.messageId / accepted / rejected / response / envelope | Same fields on SendResult |
| HTTP providers via plugins / custom | createMailer + sently/transports/<provider> |
Message fields
| Nodemailer | sently |
|---|---|
from / to / cc / bcc / replyTo | Same names on MailOptions |
subject, text, html, headers, messageId, date | Same names |
priority | "high" | "normal" | "low" |
attachments[].filename / content / path / contentType | Same names |
attachments[].cid | attachments[].contentId |
attachments[].contentDisposition: "inline" | attachments[].inline: true |
icalEvent, SOCKS proxy options | Not supported — see Non-goals |
SMTP options
| Option | Type | Default | Meaning |
|---|---|---|---|
host | string | required | Relay hostname |
port | number | 587 (465 if secure) | SMTP port |
secure | boolean | false | Implicit TLS on connect |
auth | SMTPAuth | — | { user, pass?, type?, oauth2? } |
pool | boolean | false | Connection pooling |
requireTLS | boolean | true when auth is set | Refuse AUTH on a cleartext connection |
tls | TLSOptions | — | rejectUnauthorized, servername, minVersion |
connectionTimeout | number | — | Socket connect timeout (ms) |
greetingTimeout | number | — | Wait for SMTP greeting (ms) |
socketTimeout | number | — | Idle socket timeout (ms) |
Switch to an HTTP provider later
Keep mailer.send and change only construction:
- import { createSMTPMailer } from "sently/smtp";
- const mailer = await createSMTPMailer({ host, port, auth });
+ import { createMailer } from "sently/mailer";
+ import { ResendTransport } from "sently/transports/resend";
+ const mailer = await createMailer({
+ transport: new ResendTransport({ apiKey: process.env.RESEND_API_KEY! }),
+ });Consequence: message fields stay the same; only the factory and transport change.
Troubleshooting
createMailer accepts only { transport, plugins?, hooks? }. Move host/port/auth to createSMTPMailer from sently/smtp.
Usually no. Keep from, to, subject, and body fields; change construction and sendMail → send. Rename attachment cid to contentId when you use inline images.
The factory prepares the runtime SMTP connection path (and optionally the pool) before returning. Always await it before send.
Learn more
- Email channel — mailer + transport model after you migrate
- Mail options — full
MailOptionsfield list - Attachments —
content,path, andcontentId - Entrypoints —
sently/smtpvssently/mailer - Support matrix — which runtimes and exports are supported