Sending mail from a Lumen program. One Mailer interface, one module per service, one HTML shell.
export type Mailer = {
name: string,
credential: string,
deliver: (key: string, message: MailMessage) => MailReply,
};
Ships with resend.ts and sendgrid.ts.
Use
import { MailAsk, MailPost, mailWith } from "./mail.ts";
import { resendMailer } from "./resend.ts";
let post: MailPost = {
from: "Joule <hello@joule.sh>",
replyTo: "",
key: apiKey, // already decrypted; this package reads no key store
brand: "Joule",
footer: "Sent by an agent on Joule.",
};
let ask: MailAsk = {
to: "someone@example.com, another@example.com",
subject: "Weekly report",
body: "All quiet.\n\nNothing needed your attention this week.",
};
let sent = mailWith(resendMailer(), post, ask);
if (!sent.ok) {
print(sent.fault);
}
The body is prose. Blank lines become paragraphs, single newlines become breaks, and the plain-text part is what you wrote, unchanged.
from comes from the caller, never from the message.
Refused before a request
- an address that is not one — and the whole list, not the addresses either side of the typo
- more than ten recipients
- a newline in the subject
- an empty subject or body
- a body over 100,000 characters
Answers
mailWith says whether the service accepted the message. Whether it arrived is the service's own webhooks; MailReply.id is kept so a program can ask later. SendGrid returns 202 with no body, so id is empty there.
Adding a service
A module with a deliver and a constructor, and a line wherever your program picks one. mail.ts does not change.