npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

nestjs-mailable

v1.8.0

Published

A comprehensive NestJS mail package with design patterns for email handling, templating, and multi-provider support

Readme

NestJS Mailable

Production-ready email handling for NestJS with Laravel-inspired mailable classes, fluent API, and multi-transport support.

npm version npm downloads build status typescript node license

DocumentationExamplesContributing

Features

  • Mailable Classes - Encapsulate email logic with envelope(), content(), attachments(), and headers() methods
  • Fluent API - Chainable interface: mailService.to(email).cc(cc).send(mailable)
  • Multiple Transports - SMTP, AWS SES, Mailgun, Mailjet, Resend
  • Template Engines - Handlebars, EJS, Pug with auto-detection
  • Built-in Testing - Mock MailService and verify emails without sending
  • Type-Safe - Full TypeScript support with strict typing
  • Async Configuration - Dependency injection with forRootAsync
  • Error Handling - Detailed transport-specific error messages

Installation

Install the core package:

npm install nestjs-mailable

Then install only the dependencies you need for your chosen transport and template engine.

Transport Dependencies

# SMTP
npm install nodemailer

# AWS SES (Production with nodemailer SMTP)
npm install nodemailer aws-sdk

# AWS SES (LocalStack development)
npm install aws-sdk

# Mailgun
npm install mailgun.js axios form-data

# Mailjet
npm install node-mailjet

# Resend
npm install resend

Template Engine Dependencies

# Handlebars (recommended)
npm install handlebars

# EJS
npm install ejs

# Pug
npm install pug

Example: Minimal Installation

For SMTP with Handlebars templates:

npm install nestjs-mailable nodemailer handlebars

For AWS SES with EJS templates:

npm install nestjs-mailable aws-sdk ejs

Requirements

  • Node.js >= 20.0.0
  • NestJS >= 10.0.0
  • TypeScript >= 5.0.0

Quick Start

1. Import MailModule

import { Module } from '@nestjs/common';
import { MailModule, TransportType, TEMPLATE_ENGINE } from 'nestjs-mailable';

@Module({
  imports: [
    MailModule.forRoot({
      transport: {
        type: TransportType.SMTP,
        host: 'smtp.gmail.com',
        port: 587,
        secure: false,
        auth: {
          user: process.env.MAIL_USER,
          pass: process.env.MAIL_PASS,
        },
      },
      from: {
        address: '[email protected]',
        name: 'Your App',
      },
      templates: {
        engine: TEMPLATE_ENGINE.HANDLEBARS,
        directory: './templates',
      },
    }),
  ],
})
export class AppModule {}

2. Create a Mailable Class

import {
  MailableClass as Mailable,
  MailableEnvelope,
  MailableContent,
} from 'nestjs-mailable';

export class WelcomeEmail extends Mailable {
  constructor(private user: { name: string; email: string }) {
    super();
  }

  envelope(): MailableEnvelope {
    return {
      subject: `Welcome, ${this.user.name}!`,
    };
  }

  content(): MailableContent {
    return {
      template: 'emails/welcome',
      with: {
        name: this.user.name,
        loginUrl: 'https://example.com/login',
      },
    };
  }
}

3. Send Email

import { Injectable } from '@nestjs/common';
import { MailService } from 'nestjs-mailable';
import { WelcomeEmail } from './welcome.email';

@Injectable()
export class UserService {
  constructor(private mailService: MailService) {}

  async registerUser(userData: any) {
    const user = await this.createUser(userData);
    await this.mailService.to(user.email).send(new WelcomeEmail(user));
    return user;
  }
}

4. Create Template

Create templates/emails/welcome.hbs:

<h1>Welcome, {{name}}!</h1>
<p>Get started by logging in below.</p>
<a href="{{loginUrl}}">Login to Your Account</a>

Configuration

Synchronous Configuration

MailModule.forRoot({
  transport: {
    type: TransportType.SMTP,
    host: 'smtp.example.com',
    port: 587,
    secure: false,
    auth: {
      user: 'username',
      pass: 'password',
    },
  },
  from: {
    address: '[email protected]',
    name: 'Your App',
  },
  templates: {
    engine: TEMPLATE_ENGINE.HANDLEBARS,
    directory: './templates',
  },
})

Asynchronous Configuration

import { ConfigModule, ConfigService } from '@nestjs/config';

MailModule.forRootAsync({
  imports: [ConfigModule],
  useFactory: async (configService: ConfigService) => ({
    transport: {
      type: configService.get('MAIL_TRANSPORT') as TransportType,
      host: configService.get('MAIL_HOST'),
      port: configService.get('MAIL_PORT', 587),
      secure: configService.get('MAIL_SECURE', false),
      auth: {
        user: configService.get('MAIL_USER'),
        pass: configService.get('MAIL_PASS'),
      },
    },
    from: {
      address: configService.get('MAIL_FROM_ADDRESS'),
      name: configService.get('MAIL_FROM_NAME'),
    },
    templates: {
      engine: TEMPLATE_ENGINE.HANDLEBARS,
      directory: path.join(__dirname, '../templates'),
    },
  }),
  inject: [ConfigService],
})

Transports

SMTP

MailModule.forRoot({
  transport: {
    type: TransportType.SMTP,
    host: 'smtp.gmail.com',
    port: 587,
    secure: false,
    auth: {
      user: process.env.MAIL_USER,
      pass: process.env.MAIL_PASS,
    },
  },
})

AWS SES

Production Mode (nodemailer SMTP):

MailModule.forRoot({
  transport: {
    type: TransportType.SES,
    region: 'us-east-1',
    credentials: {
      user: process.env.MAIL_USERNAME,
      pass: process.env.MAIL_PASSWORD,
    },
  },
})

LocalStack Mode (AWS SDK):

MailModule.forRoot({
  transport: {
    type: TransportType.SES,
    region: 'us-east-1',
    endpoint: 'http://localhost:4566',
    credentials: {
      accessKeyId: 'test',
      secretAccessKey: 'test',
    },
  },
})

Mailgun

MailModule.forRoot({
  transport: {
    type: TransportType.MAILGUN,
    options: {
      domain: 'mg.yourdomain.com',
      apiKey: process.env.MAILGUN_API_KEY,
    },
  },
})

Mailjet

MailModule.forRoot({
  transport: {
    type: TransportType.MAILJET,
    options: {
      apiKey: process.env.MAILJET_API_KEY,
      apiSecret: process.env.MAILJET_API_SECRET,
    },
  },
})

Resend

MailModule.forRoot({
  transport: {
    type: TransportType.RESEND,
    apiKey: process.env.RESEND_API_KEY,
  },
})

API

MailService

// Set recipients
mailService.to(email: string | string[])
mailService.cc(email: string | string[])
mailService.bcc(email: string | string[])

// Configure email
mailService.from(address: string | Address)
mailService.replyTo(address: string | Address)
mailService.subject(subject: string)
mailService.html(content: string)
mailService.text(content: string)
mailService.template(name: string, context?: Record<string, any>)

// Send
mailService.send(mailable?: Mailable): Promise<unknown>

// Testing
mailService.fake(): MailFake
mailService.clearSent(): void

Mailable Class

export class MyEmail extends Mailable {
  envelope(): MailableEnvelope {
    return { subject: 'Email Subject' };
  }

  content(): MailableContent {
    return {
      template: 'email-template',
      with: { variable: 'value' },
    };
  }

  attachments(): MailableAttachment[] {
    return [AttachmentBuilder.fromPath('./file.pdf').as('file.pdf').build()];
  }

  headers(): MailableHeaders {
    return { 'X-Custom-Header': 'value' };
  }
}

Testing

Using MailFake

import { Test } from '@nestjs/testing';
import { MailService } from 'nestjs-mailable';

describe('UserService', () => {
  let mailService: MailService;

  beforeEach(async () => {
    const module = await Test.createTestingModule({
      providers: [UserService, MailService],
    }).compile();

    mailService = module.get<MailService>(MailService);
    mailService.fake();
  });

  it('should send welcome email', async () => {
    await userService.registerUser(user);

    expect(mailService.hasSent(WelcomeEmail)).toBe(true);
    expect(mailService.hasSentTo(user.email)).toBe(true);
  });
});

Jest Mocking

import { createMailServiceMock } from 'nestjs-mailable/testing';

const mockMailService = createMailServiceMock();

await mockMailService
  .to('[email protected]')
  .subject('Test')
  .send();

expect(mockMailService.to).toHaveBeenCalledWith('[email protected]');

See the Jest Mocking Guide and Testing Utilities Reference for comprehensive testing documentation.

Template Engines

Handlebars

MailModule.forRoot({
  templates: {
    engine: TEMPLATE_ENGINE.HANDLEBARS,
    directory: './templates',
    partials: {
      header: './templates/partials/header',
      footer: './templates/partials/footer',
    },
    options: {
      helpers: {
        currency: (amount: number) => `$${amount.toFixed(2)}`,
      },
    },
  },
})

EJS

MailModule.forRoot({
  templates: {
    engine: TEMPLATE_ENGINE.EJS,
    directory: './templates',
    options: {
      cache: true,
      compileDebug: false,
    },
  },
})

Pug

MailModule.forRoot({
  templates: {
    engine: TEMPLATE_ENGINE.PUG,
    directory: './templates',
    options: {
      pretty: false,
      compileDebug: false,
    },
  },
})

Advanced Usage

Attachments

import { AttachmentBuilder } from 'nestjs-mailable';

export class InvoiceEmail extends Mailable {
  attachments(): MailableAttachment[] {
    return [
      AttachmentBuilder
        .fromPath('./invoices/invoice.pdf')
        .as('Invoice.pdf')
        .withMime('application/pdf')
        .build(),
      AttachmentBuilder
        .fromBuffer(buffer, 'image.png')
        .withMime('image/png')
        .inline('logo')
        .build(),
    ];
  }
}

Custom Headers

export class CustomEmail extends Mailable {
  headers(): MailableHeaders {
    return {
      'X-Priority': '1',
      'X-Custom-Header': 'custom-value',
    };
  }
}

Dynamic Template Selection

export class LocalizedEmail extends Mailable {
  constructor(private user: User, private locale: string) {
    super();
  }

  content(): MailableContent {
    return {
      template: `emails/${this.locale}/welcome`,
      with: { name: this.user.name },
    };
  }
}

Performance & Best Practices

Connection Pooling

MailModule.forRoot({
  transport: {
    type: TransportType.SMTP,
    pool: true,
    maxConnections: 5,
    maxMessages: 100,
    rateDelta: 1000,
    rateLimit: 5,
  },
})

Template Caching

MailModule.forRoot({
  templates: {
    engine: TEMPLATE_ENGINE.HANDLEBARS,
    options: {
      cache: true,
    },
  },
})

Async Email Sending

Consider using a queue system for non-critical emails:

import { InjectQueue } from '@nestjs/bull';
import { Queue } from 'bull';

@Injectable()
export class NotificationService {
  constructor(@InjectQueue('email') private emailQueue: Queue) {}

  async sendWelcomeEmail(user: User) {
    await this.emailQueue.add('welcome', {
      email: user.email,
      name: user.name,
    });
  }
}

Security

  • Never commit credentials. Use environment variables
  • Validate and sanitize all user input before sending
  • Use TLS for all SMTP connections in production
  • Rotate credentials regularly
  • Monitor bounce rates and complaints

Troubleshooting

Template Engine Not Found

Install the missing engine:

npm install handlebars
npm install ejs
npm install pug

SMTP Connection Timeout

  • Verify host and port are correct
  • Check firewall allows outbound SMTP connections
  • Use secure: true for port 465, secure: false for port 587

AWS SES Authentication Failed

  • Use SES SMTP credentials (not regular AWS credentials)
  • Verify email address or domain in AWS SES console
  • Ensure IAM user has ses:SendEmail permission
  • Check region matches SMTP endpoint

Template Not Found

  • Verify template directory path
  • Check file extension matches engine (.hbs, .ejs, .pug)
  • Ensure template files are included in build output

Mailgun API Error

  • Verify API key format: key-xxxxxxxxx
  • Check domain is configured in Mailgun dashboard
  • Verify sender email matches verified domain

Documentation

Complete documentation available at mahmudulazamshohan.github.io/nestjs-mailable

Examples

Complete working examples available in examples/nestjs-email-app:

  • Basic NestJS integration
  • Multiple transport configurations
  • Template engine usage
  • Mailable class implementations
  • Testing patterns

Migration

From @nestjs-modules/mailer

// Before
await this.mailerService.sendMail({
  to: user.email,
  subject: 'Welcome',
  template: './welcome',
  context: { name: user.name },
});

// After
await this.mailService
  .to(user.email)
  .send(new WelcomeEmail(user));

From nodemailer

// Before
const transporter = nodemailer.createTransport(config);
await transporter.sendMail({ from, to, subject, html });

// After
await this.mailService
  .to(to)
  .subject(subject)
  .html(html)
  .send();

Contributing

Contributions are welcome. Please read our Contributing Guide for details on:

  • Code of Conduct
  • Development setup
  • Submitting pull requests
  • Reporting bugs

Development

# Install dependencies
yarn install

# Run tests
yarn test

# Run tests with coverage
yarn test:coverage

# Build
yarn build

# Lint
yarn lint

Support

License

MIT - see LICENSE file for details.

Copyright (c) 2024 NestJS Mailable contributors