@veap/mail separates the MailMessage contract from delivery. A custom adapter implements IMailer and is registered under the CUSTOM_MAILER token when MAIL_TRANSPORT=custom.
Message contract#
A MailMessage includes to, subject, optional text or html, from, cc, bcc, replyTo, attachments, priority, and headers. Recipients can be strings or { name, address } objects. Attachments accept either inline content or a file path.
Implement the adapter#
The exact IMailer interface is exported from @veap/mail; implement its required method(s) against the declaration shipped with the version you install. The adapter should translate Veap's provider-neutral message shape into your provider SDK's shape and should not leak provider-specific fields into application services.
Register the custom provider#
import { Application, ServiceProvider } from "@veap/kernel";
import { CUSTOM_MAILER, type IMailer, MailServiceProvider } from "@veap/mail";
import { MyMailAdapter } from "./MyMailAdapter.js";
class CustomMailerProvider extends ServiceProvider {
register() {
this.container.register({
token: CUSTOM_MAILER,
useClass: MyMailAdapter,
singleton: true,
});
}
}
const app = Application.configure()
.use(CustomMailerProvider)
.use(MailServiceProvider)
.create();Set:
MAIL_TRANSPORT=customThe custom binding must exist before MailServiceProvider.boot() selects the transport. If MAIL_TRANSPORT=custom is set without a binding under CUSTOM_MAILER, boot throws an error.
Production checklist#
- Configure provider credentials through secret storage.
- Set request timeouts and retries in the adapter or provider SDK.
- Do not log message bodies or verification codes.
- Handle provider errors explicitly and monitor delivery.
- Test text, HTML, attachments, and recipient variants.