Skip to content

Sending Emails ​

Inject the Service ​

typescript
import { Injectable } from '@nestjs/common';
import { MailerService } from '@nestjs-modules/mailer';

@Injectable()
export class NotificationService {
  constructor(private readonly mailerService: MailerService) {}
}

sendMail() accepts the same fields as nodemailer email message.

Plain Text / HTML ​

Send a simple email without templates:

typescript
await this.mailerService.sendMail({
  to: 'user@example.com',
  from: 'noreply@example.com',
  subject: 'Hello World',
  text: 'Plain text content',
  html: '<b>HTML content</b>',
});

Context Placeholders ​

When context is set, {{key}} placeholders are replaced in the subject, in an inline html string (only when no template is used) and in a textTemplate file. Only the context's own properties are used; unknown keys are left as they are.

In html, {{key}} is HTML-escaped. Use {{{key}}} to insert markup you trust:

typescript
await this.mailerService.sendMail({
  to: 'user@example.com',
  subject: 'Welcome {{name}}',
  html: '<p>Hi {{name}}</p>{{{signature}}}',
  context: {
    name: user.name, // escaped
    signature: '<b>The Team</b>', // inserted as-is
  },
});

Changed in 3.0

Before 3.0, {{key}} was inserted into html without escaping. If you relied on that to inject markup, switch those placeholders to {{{key}}}.

Safe HTML with the html Tag ​

For HTML built in code, the html tagged template escapes every interpolated value, so user data can't inject markup. Arrays are joined, null, undefined and false render nothing, and nested html values (or trusted strings wrapped in unsafeHtml()) are kept as they are:

typescript
import { html, unsafeHtml } from '@nestjs-modules/mailer';

await this.mailerService.sendMail({
  to: user.email,
  subject: 'Your order',
  html: html`
    <p>Hi ${user.name},</p>
    <ul>${order.items.map((item) => html`<li>${item.name} × ${item.quantity}</li>`)}</ul>
    ${unsafeHtml(footerFromOurOwnTemplate)}
  `,
});

html returns a SafeHtml value (check one with isSafeHtml()). sendMail(), send() and mail classes accept it as html, and also accept @nestjs/mail's own SafeHtml. Use unsafeHtml() only for markup you control, never for user input.

Plain-Text Part ​

An email with HTML but no text gets a plain-text part derived from the final HTML (after templates and plugins ran): paragraphs and line breaks kept, links written as text (url), list items as - item, styles and scripts dropped, entities decoded. Spam filters and text-only clients prefer emails that have one.

It is on by default; an explicit text (or textTemplate) is always kept, and raw messages are left alone. Turn it off with textFromHtml: false. The converter is exported as htmlToText(html).

With Templates ​

Use your configured template engine. The file extension (.hbs, .pug, .ejs) is appended automatically:

typescript
await this.mailerService.sendMail({
  to: 'user@example.com',
  subject: 'Welcome!',
  template: 'welcome',       // resolves to templates/welcome.hbs
  context: {                  // data passed to the template
    name: 'John Doe',
    code: 'cf1a3f828287',
  },
});

With Absolute Path ​

You can use an absolute path to the template file:

typescript
await this.mailerService.sendMail({
  to: 'user@example.com',
  subject: 'Welcome!',
  template: `${__dirname}/welcome`,
  context: {
    name: 'John Doe',
  },
});

TIP

When using absolute paths, make sure template files are copied to the dist folder at build time. See Copy Templates to dist.

Attachments ​

Send emails with file attachments:

typescript
await this.mailerService.sendMail({
  to: 'user@example.com',
  subject: 'Invoice',
  html: '<p>Please find your invoice attached.</p>',
  attachments: [
    {
      filename: 'invoice.pdf',
      path: '/absolute/path/to/invoice.pdf',
    },
  ],
});

Multiple Attachments ​

typescript
await this.mailerService.sendMail({
  to: 'user@example.com',
  subject: 'Report',
  template: 'report',
  context: { title: 'Monthly Report' },
  attachments: [
    // File from disk
    { filename: 'report.pdf', path: '/path/to/report.pdf' },
    // Buffer
    { filename: 'data.csv', content: Buffer.from('id,name\n1,John') },
    // String content
    { filename: 'notes.txt', content: 'Some notes here' },
    // URL (nodemailer will fetch it)
    { filename: 'logo.png', path: 'https://example.com/logo.png' },
  ],
});

Inline Images (Embedded) ​

Embed images directly in the HTML using cid:

typescript
await this.mailerService.sendMail({
  to: 'user@example.com',
  subject: 'Newsletter',
  html: '<img src="cid:logo" alt="Logo" />',
  attachments: [
    {
      filename: 'logo.png',
      path: '/path/to/logo.png',
      cid: 'logo',
    },
  ],
});

See Nodemailer attachments docs for all options.

Custom Headers ​

typescript
await this.mailerService.sendMail({
  to: 'user@example.com',
  subject: 'Hello',
  html: '<p>Hello!</p>',
  headers: {
    'X-Custom-Header': 'custom-value',
    'X-Priority': '1',
  },
});

Reply-To and Threading ​

typescript
await this.mailerService.sendMail({
  to: 'user@example.com',
  replyTo: 'support@example.com',
  subject: 'Re: Your ticket',
  html: '<p>We received your request.</p>',
  inReplyTo: '<original-message-id@example.com>',
  references: ['<original-message-id@example.com>'],
});

Using a Specific Transporter ​

When you have multiple transporters configured, specify which one to use:

typescript
await this.mailerService.sendMail({
  transporterName: 'secondary',
  to: 'user@example.com',
  subject: 'Sent via secondary SMTP',
  html: '<p>Hello!</p>',
});

See Multiple Transporters for setup instructions.

Delivery Options ​

v4 adds per-message options that control how an email is delivered. They are not passed to nodemailer:

OptionDescription
retryOverrides the module's retries: a number of attempts, false, or options (Reliability)
timeoutPer-attempt timeout in ms, overriding sendTimeout (Reliability)
failoverNamed transporters to try when the selected one fails, or false (Reliability)
signalAn AbortSignal that cancels the send and the wait between retries (Reliability)
idempotencyKeyDerives a stable Message-ID so a redelivery can be deduplicated (Reliability)
unsubscribeAdds one-click List-Unsubscribe headers (Delivery rules)

Mail Classes ​

For emails you send from several places, a mail class keeps the subject, template and context in one typed provider:

typescript
await this.mailerService.send(WelcomeMail, { to: user.email, data: user });

Released under the MIT License.