New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

@softvence/mail

Package Overview
Dependencies
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@softvence/mail

Mail service for softvence

latest
Source
npmnpm
Version
0.0.5
Version published
Maintainers
1
Created
Source

@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" <noreply@myapp.com>',
      },
    }),
  ],
})
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

OptionTypeRequiredDescription
transportSMTPTransport | SMTPTransport.OptionsNodemailer transport configuration
defaultsnodemailer.Transporter['options']Default mail options (from, etc.)
templateRenderer(type, context) => stringCustom template renderer function
mailQueueboolean | QueueConfigMail queue configuration

Transport Configuration

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

Gmail

transport: {
  service: 'gmail',
  auth: {
    user: 'your-email@gmail.com',
    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

OptionTypeDescription
tostring | string[]Recipient email address(es)
subjectstringEmail subject
templateMailTemplateTypeBuilt-in template name (e.g., 'otp')
contextMailContextTemplate context data
htmlstringRaw HTML content (overrides template)
textstringPlain text content
fromstringSender email (overrides default)
ccstring | string[]CC recipients
bccstring | string[]BCC recipients
replyTostringReply-to address
priority'high' | 'normal' | 'low'Email priority
attachmentsany[]Email attachments
headersRecord<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: "user@example.com",
  subject: "Welcome!",
  template: "welcome",
  context: { name: "John Doe" },
});

// Send password reset email
await this.mailService.send({
  to: "user@example.com",
  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_USER=your-email@gmail.com
EMAIL_PASSWORD=your-app-password
EMAIL_FROM="My App" <noreply@myapp.com>

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: "user@example.com",
  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: "user@example.com",
  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:

  • Enable 2-Factor Authentication
  • Go to Google Account → Security → App Passwords
  • Generate a new app password
  • 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" <support@myapp.com>',
}

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: 'your-email@gmail.com',
    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: 'your-email@outlook.com',
    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

Keywords

mail

FAQs

Package last updated on 29 Nov 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts