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

@softvence/mail

v0.0.5

Published

Mail service for softvence

Readme

@softvence/mail

A powerful and flexible NestJS mail service built on top of Nodemailer with built-in template support, custom template rendering, and optional queue integration.

Features

Easy Integration - Seamlessly integrates with NestJS applications
📧 Nodemailer Powered - Built on the reliable Nodemailer library
🎨 Template System - Built-in OTP template with customizable template renderer
🔧 Flexible Configuration - Support for all major email providers (Gmail, SendGrid, etc.)
📬 Rich Email Options - Support for attachments, CC, BCC, priority, and custom headers
Queue Support - Optional mail queue integration for async processing
📦 TypeScript Support - Fully typed with TypeScript for better developer experience

Installation

npm install @softvence/mail
# or
yarn add @softvence/mail
# or
pnpm add @softvence/mail

Prerequisites

  • Node.js >= 16
  • NestJS >= 11.0.0
  • Email service credentials (Gmail, SendGrid, SMTP, etc.)

Quick Start

1. Import the Module

Import MailModule in your application module and configure it with your email service credentials:

import { Module } from "@nestjs/common";
import { MailModule } from "@softvence/mail";

@Module({
  imports: [
    MailModule.forRoot({
      transport: {
        service: "gmail",
        auth: {
          user: process.env.EMAIL_USER,
          pass: process.env.EMAIL_PASSWORD,
        },
      },
      defaults: {
        from: '"My App" <[email protected]>',
      },
    }),
  ],
})
export class AppModule {}

2. Use in Your Service/Controller

Inject MailService and start sending emails:

import { Injectable } from "@nestjs/common";
import { MailService } from "@softvence/mail";

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

  async sendOTP(email: string, otp: string) {
    await this.mailService.send({
      to: email,
      subject: "Your OTP Code",
      template: "otp",
      context: {
        otp,
        expire: "5m",
      },
    });
  }
}

Configuration

MailModuleOptions

| Option | Type | Required | Description | | ------------------ | ---------------------------------------- | -------- | ---------------------------------- | | transport | SMTPTransport \| SMTPTransport.Options | ✅ | Nodemailer transport configuration | | defaults | nodemailer.Transporter['options'] | ❌ | Default mail options (from, etc.) | | templateRenderer | (type, context) => string | ❌ | Custom template renderer function | | mailQueue | boolean \| QueueConfig | ❌ | Mail queue configuration |

Transport Configuration

The transport option accepts any valid Nodemailer transport configuration. Here are common examples:

Gmail

transport: {
  service: 'gmail',
  auth: {
    user: '[email protected]',
    pass: 'your-app-password', // Use App Password, not regular password
  },
}

SendGrid

transport: {
  host: 'smtp.sendgrid.net',
  port: 587,
  auth: {
    user: 'apikey',
    pass: 'your-sendgrid-api-key',
  },
}

Custom SMTP

transport: {
  host: 'smtp.example.com',
  port: 587,
  secure: false, // true for 465, false for other ports
  auth: {
    user: 'username',
    pass: 'password',
  },
}

MailSendOptions

| Option | Type | Description | | ------------- | ----------------------------- | ------------------------------------- | | to | string \| string[] | Recipient email address(es) | | subject | string | Email subject | | template | MailTemplateType | Built-in template name (e.g., 'otp') | | context | MailContext | Template context data | | html | string | Raw HTML content (overrides template) | | text | string | Plain text content | | from | string | Sender email (overrides default) | | cc | string \| string[] | CC recipients | | bcc | string \| string[] | BCC recipients | | replyTo | string | Reply-to address | | priority | 'high' \| 'normal' \| 'low' | Email priority | | attachments | any[] | Email attachments | | headers | Record<string, string> | Custom email headers |

TTL Constants

The package includes predefined TTL (Time To Live) constants for OTP expiration:

import { TTL } from "@softvence/mail";

// Seconds: '10s', '30s'
// Minutes: '1m', '2m', '3m', '4m', '5m', '10m', '15m', '30m'
// Hours: '1h', '2h', '6h', '12h'
// Days: '1d', '3d', '7d', '14d', '30d'
// Weeks: '1w', '2w', '4w'
// Months: '1mo', '3mo', '6mo'

Usage Examples

Basic Email Sending

import { Injectable } from "@nestjs/common";
import { MailService } from "@softvence/mail";

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

  async sendWelcomeEmail(email: string, name: string) {
    await this.mailService.send({
      to: email,
      subject: "Welcome to Our Platform!",
      html: `<h1>Hello ${name}!</h1><p>Welcome to our platform.</p>`,
    });
  }
}

Using Built-in OTP Template

import { Injectable } from "@nestjs/common";
import { MailService } from "@softvence/mail";

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

  async sendOTP(email: string) {
    const otp = this.generateOTP(); // Your OTP generation logic

    await this.mailService.send({
      to: email,
      subject: "Your Verification Code",
      template: "otp",
      context: {
        otp,
        expire: "5m", // OTP expires in 5 minutes
      },
    });
  }

  private generateOTP(): string {
    return Math.floor(100000 + Math.random() * 900000).toString();
  }
}

Sending to Multiple Recipients

async sendBulkNotification(emails: string[], message: string) {
  await this.mailService.send({
    to: emails, // Array of email addresses
    subject: 'Important Notification',
    html: `<p>${message}</p>`,
  });
}

Email with Attachments

import * as fs from 'fs';

async sendInvoice(email: string, invoicePath: string) {
  await this.mailService.send({
    to: email,
    subject: 'Your Invoice',
    html: '<p>Please find your invoice attached.</p>',
    attachments: [
      {
        filename: 'invoice.pdf',
        path: invoicePath,
      },
      {
        filename: 'logo.png',
        content: fs.readFileSync('./assets/logo.png'),
      },
    ],
  });
}

Email with CC and BCC

async sendReport(to: string, cc: string[], bcc: string[]) {
  await this.mailService.send({
    to,
    cc,
    bcc,
    subject: 'Monthly Report',
    html: '<h1>Monthly Report</h1><p>Report content here...</p>',
    priority: 'high',
  });
}

Custom Template Renderer

Create custom templates by providing a templateRenderer function:

import { Module } from "@nestjs/common";
import { MailModule } from "@softvence/mail";

@Module({
  imports: [
    MailModule.forRoot({
      transport: {
        service: "gmail",
        auth: {
          user: process.env.EMAIL_USER,
          pass: process.env.EMAIL_PASSWORD,
        },
      },
      templateRenderer: (type, context) => {
        switch (type) {
          case "otp":
            return `
              <div style="font-family: Arial, sans-serif;">
                <h1>Your OTP Code</h1>
                <p>Your verification code is: <strong>${context.otp}</strong></p>
                <p>Expires in: ${context.expire}</p>
              </div>
            `;
          case "welcome":
            return `
              <div style="font-family: Arial, sans-serif;">
                <h1>Welcome ${context.name}!</h1>
                <p>Thank you for joining our platform.</p>
              </div>
            `;
          case "password-reset":
            return `
              <div style="font-family: Arial, sans-serif;">
                <h1>Password Reset Request</h1>
                <p>Click the link below to reset your password:</p>
                <a href="${context.resetLink}">Reset Password</a>
              </div>
            `;
          default:
            throw new Error(`Unknown template type: ${type}`);
        }
      },
    }),
  ],
})
export class AppModule {}

Then use your custom templates:

// Send welcome email
await this.mailService.send({
  to: "[email protected]",
  subject: "Welcome!",
  template: "welcome",
  context: { name: "John Doe" },
});

// Send password reset email
await this.mailService.send({
  to: "[email protected]",
  subject: "Reset Your Password",
  template: "password-reset",
  context: { resetLink: "https://example.com/reset?token=abc123" },
});

Using Environment Variables

Create a .env file:

EMAIL_SERVICE=gmail
[email protected]
EMAIL_PASSWORD=your-app-password
EMAIL_FROM="My App" <[email protected]>

Configure the module:

import { Module } from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";
import { MailModule } from "@softvence/mail";

@Module({
  imports: [
    ConfigModule.forRoot(),
    MailModule.forRoot({
      transport: {
        service: process.env.EMAIL_SERVICE,
        auth: {
          user: process.env.EMAIL_USER,
          pass: process.env.EMAIL_PASSWORD,
        },
      },
      defaults: {
        from: process.env.EMAIL_FROM,
      },
    }),
  ],
})
export class AppModule {}

Async Configuration

For dynamic configuration using ConfigService:

import { Module } from "@nestjs/module";
import { ConfigModule, ConfigService } from "@nestjs/config";
import { MailModule } from "@softvence/mail";

@Module({
  imports: [
    ConfigModule.forRoot(),
    {
      module: MailModule,
      imports: [ConfigModule],
      useFactory: (configService: ConfigService) => ({
        transport: {
          service: configService.get("EMAIL_SERVICE"),
          auth: {
            user: configService.get("EMAIL_USER"),
            pass: configService.get("EMAIL_PASSWORD"),
          },
        },
        defaults: {
          from: configService.get("EMAIL_FROM"),
        },
      }),
      inject: [ConfigService],
    },
  ],
})
export class AppModule {}

Complete Controller Example

import { Controller, Post, Body, BadRequestException } from "@nestjs/common";
import { MailService } from "@softvence/mail";

class SendEmailDto {
  to: string;
  subject: string;
  message: string;
}

class SendOTPDto {
  email: string;
}

@Controller("mail")
export class MailController {
  constructor(private readonly mailService: MailService) {}

  @Post("send")
  async sendEmail(@Body() dto: SendEmailDto) {
    try {
      await this.mailService.send({
        to: dto.to,
        subject: dto.subject,
        html: `<p>${dto.message}</p>`,
      });

      return {
        success: true,
        message: "Email sent successfully",
      };
    } catch (error) {
      throw new BadRequestException("Failed to send email");
    }
  }

  @Post("send-otp")
  async sendOTP(@Body() dto: SendOTPDto) {
    const otp = Math.floor(100000 + Math.random() * 900000).toString();

    await this.mailService.send({
      to: dto.email,
      subject: "Your OTP Code",
      template: "otp",
      context: {
        otp,
        expire: "5m",
      },
    });

    return {
      success: true,
      message: "OTP sent successfully",
      // In production, don't return the OTP
      // Store it in cache/database instead
    };
  }
}

Built-in Templates

OTP Template

The package includes a built-in OTP (One-Time Password) template with a professional design:

await this.mailService.send({
  to: "[email protected]",
  subject: "Your Verification Code",
  template: "otp",
  context: {
    otp: "123456",
    expire: "5m", // Any TTL constant
  },
});

The OTP template features:

  • Clean, responsive design
  • Prominent OTP display
  • Expiration time notice
  • Professional styling
  • Mobile-friendly layout

API Reference

MailService

send(options: MailSendOptions): Promise<void>

Sends an email with the specified options.

Parameters:

  • options - Mail send options (see MailSendOptions table above)

Returns: Promise that resolves when email is sent

Throws: BadGatewayException if email sending fails

Example:

await this.mailService.send({
  to: "[email protected]",
  subject: "Hello",
  html: "<p>Hello World!</p>",
});

Best Practices

1. Use Environment Variables

Never hardcode email credentials. Always use environment variables:

MailModule.forRoot({
  transport: {
    service: process.env.EMAIL_SERVICE,
    auth: {
      user: process.env.EMAIL_USER,
      pass: process.env.EMAIL_PASSWORD,
    },
  },
});

2. Use App Passwords for Gmail

If using Gmail, create an App Password instead of using your regular password:

  1. Enable 2-Factor Authentication
  2. Go to Google Account → Security → App Passwords
  3. Generate a new app password
  4. Use this password in your configuration

3. Handle Errors Gracefully

Always wrap email sending in try-catch blocks:

try {
  await this.mailService.send(options);
  this.logger.log("Email sent successfully");
} catch (error) {
  this.logger.error("Failed to send email", error);
  // Handle error appropriately
}

4. Use Templates for Consistency

Create reusable templates for common email types:

templateRenderer: (type, context) => {
  const templates = {
    otp: generateOtpTemplate,
    welcome: generateWelcomeTemplate,
    "password-reset": generatePasswordResetTemplate,
  };

  const template = templates[type];
  if (!template) {
    throw new Error(`Unknown template: ${type}`);
  }

  return template(context);
};

5. Validate Email Addresses

Validate email addresses before sending:

import { IsEmail } from "class-validator";

class SendEmailDto {
  @IsEmail()
  to: string;

  subject: string;
  message: string;
}

6. Use Queue for Bulk Emails

For sending bulk emails, consider using a queue system to avoid blocking:

MailModule.forRoot({
  transport: {
    /* ... */
  },
  mailQueue: {
    queueName: "mail-queue",
    host: "localhost",
    port: 6379,
    username: "default",
    password: "default",
  },
});

7. Test Email Configuration

Test your email configuration in development:

@Injectable()
export class MailService {
  async testConnection() {
    try {
      await this.transporter.verify();
      console.log("✅ Email server connection successful");
    } catch (error) {
      console.error("❌ Email server connection failed", error);
    }
  }
}

8. Use Proper From Addresses

Use a proper "from" address with display name:

defaults: {
  from: '"My App Support" <[email protected]>',
}

9. Implement Rate Limiting

Implement rate limiting to prevent abuse:

import { ThrottlerModule } from '@nestjs/throttler';

@Module({
  imports: [
    ThrottlerModule.forRoot({
      ttl: 60,
      limit: 10, // 10 emails per minute
    }),
  ],
})

10. Log Email Activities

Log all email activities for debugging and monitoring:

await this.mailService.send(options);
this.logger.log(`Email sent to ${options.to}: ${options.subject}`);

Error Handling

The service throws BadGatewayException when email sending fails:

import { BadGatewayException } from "@nestjs/common";

try {
  await this.mailService.send(options);
} catch (error) {
  if (error instanceof BadGatewayException) {
    console.error("Email service error:", error.message);
    // Handle email service errors
  }
  throw error;
}

TypeScript Support

All types are exported and can be imported:

import {
  MailService,
  MailModuleOptions,
  MailSendOptions,
  MailTemplateType,
  MailContext,
  TTL,
  TTLKey,
} from "@softvence/mail";

Common Email Providers

Gmail

transport: {
  service: 'gmail',
  auth: {
    user: '[email protected]',
    pass: 'your-app-password',
  },
}

SendGrid

transport: {
  host: 'smtp.sendgrid.net',
  port: 587,
  auth: {
    user: 'apikey',
    pass: process.env.SENDGRID_API_KEY,
  },
}

Mailgun

transport: {
  host: 'smtp.mailgun.org',
  port: 587,
  auth: {
    user: process.env.MAILGUN_USERNAME,
    pass: process.env.MAILGUN_PASSWORD,
  },
}

Amazon SES

transport: {
  host: 'email-smtp.us-east-1.amazonaws.com',
  port: 587,
  auth: {
    user: process.env.AWS_SES_USERNAME,
    pass: process.env.AWS_SES_PASSWORD,
  },
}

Outlook/Office 365

transport: {
  host: 'smtp.office365.com',
  port: 587,
  secure: false,
  auth: {
    user: '[email protected]',
    pass: 'your-password',
  },
}

Troubleshooting

Gmail "Less Secure Apps" Error

Solution: Use App Passwords instead of regular passwords. Enable 2FA and generate an app password.

Connection Timeout

Solution: Check your firewall settings and ensure the SMTP port is not blocked.

Authentication Failed

Solution: Verify your credentials and ensure you're using the correct username/password format for your provider.

Emails Going to Spam

Solution:

  • Use a verified domain
  • Set up SPF, DKIM, and DMARC records
  • Use a reputable email service provider
  • Avoid spam trigger words in subject/content

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT © Sabbir Hossain Shuvo

Repository

Support

For questions and support, please open an issue on GitHub.


Made with ❤️ by the Softvence Team