Mailer v4
@nestjs-modules/mailer v4 is out. It is the biggest release since the module started: typed mail classes, zero-dependency HTTP transports, delivery that survives flaky servers without sending duplicates, a transactional outbox, rules for staging and suppression lists, a real test mailbox, a browser preview, and first-class interop with the official @nestjs/mail. The sendMail() API you know is unchanged.
One principle guided the release: every capability is optional, and you choose how to use the library. Write emails with sendMail(), mail classes or the official Mailer; deliver them through nodemailer, our HTTP transports or @nestjs/mail's; send them directly, through BullMQ or through @nestjs/outbox. → Choose your setup
pnpm add @nestjs-modules/mailer@^4 nodemailerMail classes
Write an email once, as a typed provider, and send it from anywhere:
import { Injectable } from '@nestjs/common';
import { Mailable } from '@nestjs-modules/mailer';
@Injectable()
export class WelcomeMail implements Mailable<{ name: string }> {
render({ name }: { name: string }) {
return { subject: `Welcome, ${name}!`, template: 'welcome', context: { name } };
}
}
await mailerService.send(WelcomeMail, { to: 'ada@example.com', data: { name: 'Ada' } });mailerService.render() gives you the finished email without sending it. The class shape matches @nestjs/mail's, so the same class works with both. → Mail classes
HTTP transports
ResendTransport, PostmarkTransport, SendGridTransport, SesTransport (raw MIME, SigV4, no AWS SDK), MailgunTransport and BrevoTransport use Node's fetch and add no dependencies. → HTTP transports
Reliable delivery
Transient failures are now retried by default (3 attempts, exponential backoff with jitter), and only transient ones: a rejected recipient fails at once. A timeout or a connection dropped mid-message may already have been delivered, so it is retried only when the email has a stable Message-ID: no duplicates. Add failover: ['backup'] to switch transporters when one is down, an AbortSignal to give up, and an idempotencyKey to give a redelivered email the same Message-ID. Shutdown waits for sends in flight. → Reliability
Typed errors, validation, safe HTML
Failures are thrown as typed errors (MailerSmtpError, MailerTimeoutError, MailerProviderError…) with a permanent flag. Malformed addresses and header injections are rejected before sending. The html tag escapes interpolated values, and emails without a text part get one derived from the HTML. → Errors and validation · Sending emails
Transactional outbox
MailerOutboxModule records emails in your database transaction through the official @nestjs/outbox and sends them after the commit, with the outbox message id as idempotency key and permanent failures dead-lettered. → Transactional outbox
Delivery rules
intercept redirects every non-allowed recipient to a QA inbox in staging. suppress drops bounced, complained or unsubscribed addresses before they hurt your reputation. unsubscribe adds the one-click List-Unsubscribe headers Gmail and Yahoo require from bulk senders. → Delivery rules
Testing and local development
MailerTestModule now accepts your real options and provides a TestMailbox:
const mail = mailbox.assertSent({ to: 'ada@example.com', mail: WelcomeMail });
const token = mail.link('/verify').searchParams.get('token');FileTransport writes .eml files, LogTransport logs a summary, and MailerPreviewModule serves your emails at /_mailer. → Testing · Dev transports · Email previews
Events, queue and i18n
Events gain RETRY, FAILOVER and SUPPRESSED, richer payloads, an RxJS events$ stream and diagnostics_channel publishing, and @nestjs/event-emitter listeners now actually fire. The BullMQ queue fails permanent errors at once, deduplicates redeliveries and can schedule() emails. i18n falls back from es-CO to es. → Events · Queue · i18n
Working with @nestjs/mail
The NestJS team recently announced @nestjs/mail, a dependency-free official mail package. We think that is great news, and v4 is designed to work with it rather than against it. The new @nestjs-modules/mailer/nestjs-mail entry point provides:
NodemailerMailTransport: any nodemailer transport for@nestjs/mail;MailerServiceTransport:@nestjs/maildelivering throughMailerModule, with failover, interception and suppression;TemplateAdapterEngine: Handlebars, Pug, EJS, MJML, Liquid and Nunjucks templates for@nestjs/mail.
→ Interop with @nestjs/mail · Honest comparison
Upgrading
Most apps upgrade without code changes. The defaults to know: retries are on (retry: false keeps the v3 behavior), errors are typed (wrapErrors: false), messages are validated (validate: false) and HTML emails get a text part (textFromHtml: false). Event payloads have extra fields, MailerTestModule delivers to a TestMailbox, and timeouts are retried only with a stable Message-ID. → Migration guide
Thanks to everyone who reported issues and sent pull requests. Feedback is welcome on GitHub.