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

@mirad-work/sms-nestjs

v0.3.0

Published

NestJS adapter for @mirad-work/sms-core

Readme

@mirad-work/sms-nestjs

A comprehensive NestJS adapter for @mirad-work/sms-core - providing seamless SMS functionality for NestJS applications with support for multiple Iranian SMS providers.

🚀 Features

  • 🏗️ NestJS Integration: First-class NestJS module with dependency injection support
  • 🔧 Multiple Configuration Options: Sync, async, factory-based, and environment-based configuration
  • 📱 Multi-Provider Support: Kavenegar, SMS.ir, Melipayamak, IPPanel, and Mock (for testing)
  • 🛡️ Robust Error Handling: Comprehensive error handling with proper NestJS exceptions
  • 🔄 Retry & Fallback: Built-in retry mechanisms and fallback driver support
  • 📊 Bulk Operations: Efficient bulk SMS sending with concurrency control
  • 🩺 Health Checks: Built-in health monitoring and service status endpoints
  • 📝 Comprehensive Logging: Detailed logging for debugging and monitoring
  • 🧪 Testing Ready: Mock driver and testing utilities included
  • 📖 TypeScript First: Full TypeScript support with comprehensive type definitions

📦 Installation

npm install @mirad-work/sms-nestjs @mirad-work/sms-core
# or
yarn add @mirad-work/sms-nestjs @mirad-work/sms-core

Peer Dependencies

Make sure you have these NestJS packages installed:

npm install @nestjs/common @nestjs/config

🏁 Quick Start

1. Basic Setup with Environment Variables

First, create your .env file:

# SMS Configuration
SMS_DEFAULT_DRIVER=kavenegar
SMS_TIMEOUT=10000

# Kavenegar Configuration
SMS_KAVENEGAR_API_KEY=your-kavenegar-api-key
SMS_KAVENEGAR_LINE_NUMBER=your-line-number

# Or for SMS.ir
# SMS_DEFAULT_DRIVER=smsir
# SMS_SMSIR_API_KEY=your-smsir-api-key
# SMS_SMSIR_LINE_NUMBER=your-line-number

2. Configure Your Module

import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { SmsModule } from '@mirad-work/sms-nestjs';

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    SmsModule.forEnvironment({ isGlobal: true }),
  ],
})
export class AppModule {}

3. Use in Your Service

import { Injectable } from '@nestjs/common';
import { SmsService } from '@mirad-work/sms-nestjs';

@Injectable()
export class AuthService {
  constructor(private readonly smsService: SmsService) {}

  async sendOtp(phoneNumber: string, code: string) {
    const message = this.smsService.createVerificationMessage(
      phoneNumber,
      'otp-verification', // Your template name
      { code } // Template variables
    );

    return await this.smsService.verify(message);
  }
}

📋 Configuration Options

Environment-based Configuration (Recommended)

import { SmsModule } from '@mirad-work/sms-nestjs';
import { ConfigModule } from '@nestjs/config';

@Module({
  imports: [
    ConfigModule.forRoot(),
    SmsModule.forEnvironment({ isGlobal: true }),
  ],
})
export class AppModule {}

Synchronous Configuration

import { SmsModule, DriverType } from '@mirad-work/sms-nestjs';

@Module({
  imports: [
    SmsModule.forRoot({
      config: {
        defaultDriver: DriverType.KAVENEGAR,
        timeout: 10000,
        drivers: {
          kavenegar: {
            url: 'https://api.kavenegar.com/v1/',
            apiKey: 'your-api-key',
            lineNumber: 'your-line-number',
          },
        },
      },
      isGlobal: true,
    }),
  ],
})
export class AppModule {}

Asynchronous Configuration with Factory

import { SmsModule, NestSmsConfigHelper } from '@mirad-work/sms-nestjs';
import { ConfigModule, ConfigService } from '@nestjs/config';

@Module({
  imports: [
    ConfigModule.forRoot(),
    SmsModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: (configService: ConfigService) =>
        NestSmsConfigHelper.createFromConfigService(configService),
      inject: [ConfigService],
      isGlobal: true,
    }),
  ],
})
export class AppModule {}

Custom Configuration Factory

import { Injectable } from '@nestjs/common';
import { 
  SmsOptionsFactory, 
  SmsModule, 
  NestSmsConfigHelper 
} from '@mirad-work/sms-nestjs';
import { ConfigService } from '@nestjs/config';
import { ISmsConfig } from '@mirad-work/sms-core';

@Injectable()
export class SmsConfigService implements SmsOptionsFactory {
  constructor(private configService: ConfigService) {}

  createSmsOptions(): ISmsConfig {
    const environment = this.configService.get('NODE_ENV');
    
    if (environment === 'test') {
      return NestSmsConfigHelper.createForTesting();
    }
    
    return NestSmsConfigHelper.createFromConfigService(this.configService);
  }
}

@Module({
  imports: [
    SmsModule.forRootAsync({
      useClass: SmsConfigService,
      isGlobal: true,
    }),
  ],
  providers: [SmsConfigService],
})
export class AppModule {}

🔧 Environment Variables

| Variable | Description | Default | |----------|-------------|---------| | SMS_DEFAULT_DRIVER | Default SMS driver | kavenegar | | SMS_TIMEOUT | Request timeout (ms) | 10000 | | Kavenegar | | SMS_KAVENEGAR_URL | Kavenegar API URL | https://api.kavenegar.com/v1/ | | SMS_KAVENEGAR_API_KEY | Kavenegar API key | - | | SMS_KAVENEGAR_LINE_NUMBER | Kavenegar line number | - | | SMS.ir | | SMS_SMSIR_URL | SMS.ir API URL | https://api.sms.ir/v1/ | | SMS_SMSIR_API_KEY | SMS.ir API key | - | | SMS_SMSIR_LINE_NUMBER | SMS.ir line number | - | | Melipayamak | | SMS_MELIPAYAMAK_URL | Melipayamak API URL | https://console.melipayamak.com/api/ | | SMS_MELIPAYAMAK_API_KEY | Melipayamak API key | - | | SMS_MELIPAYAMAK_LINE_NUMBER | Melipayamak line number | - | | IPPanel | | SMS_IPPANEL_URL | IPPanel API URL | https://api2.ippanel.com/ | | SMS_IPPANEL_API_KEY | IPPanel API key | - | | SMS_IPPANEL_LINE_NUMBER | IPPanel line number | - | | Mock (Testing) | | SMS_USE_MOCK | Use mock driver | false | | SMS_MOCK_SHOULD_FAIL | Mock driver should fail | false | | SMS_MOCK_DELAY | Mock driver delay (ms) | 0 |

🚀 Usage Examples

Basic SMS Sending

@Injectable()
export class NotificationService {
  constructor(private smsService: SmsService) {}

  async sendWelcome(phoneNumber: string, name: string) {
    const response = await this.smsService.verify({
      to: phoneNumber,
      template: 'welcome',
      tokens: { name },
    });

    if (response.success) {
      console.log(`Welcome SMS sent: ${response.messageId}`);
    } else {
      console.error(`Failed to send SMS: ${response.error}`);
    }

    return response;
  }
}

Bulk SMS Operations

async sendBulkNotifications(recipients: Array<{phone: string, code: string}>) {
  const messages = recipients.map(recipient => ({
    to: recipient.phone,
    template: 'otp-verification',
    tokens: { code: recipient.code },
  }));

  const responses = await this.smsService.verifyBulk(messages, {
    concurrency: 5, // Send 5 at a time
    failFast: false, // Continue even if some fail
  });

  const successful = responses.filter(r => r.success).length;
  console.log(`Sent ${successful}/${responses.length} SMS messages`);

  return responses;
}

Health Monitoring

@Controller('health')
export class HealthController {
  constructor(private smsService: SmsService) {}

  @Get('sms')
  async checkSmsHealth() {
    const health = await this.smsService.healthCheck();
    
    if (health.status === 'unhealthy') {
      throw new ServiceUnavailableException('SMS service is unhealthy');
    }
    
    return health;
  }
}

Advanced Error Handling

@Injectable()
export class SmsNotificationService {
  constructor(private smsService: SmsService) {}

  async sendWithRetry(phoneNumber: string, template: string, tokens: any) {
    const maxRetries = 3;
    let lastError: Error;

    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      try {
        const response = await this.smsService.verify({
          to: phoneNumber,
          template,
          tokens,
        });

        if (response.success) {
          return response;
        } else {
          // Provider returned error - don't retry
          throw new Error(response.error || 'SMS sending failed');
        }
      } catch (error) {
        lastError = error;
        
        if (attempt < maxRetries) {
          // Exponential backoff
          await this.delay(Math.pow(2, attempt) * 1000);
        }
      }
    }

    throw lastError;
  }

  private delay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

🧪 Testing

Setup for Testing

import { Test } from '@nestjs/testing';
import { SmsModule, SmsService } from '@mirad-work/sms-nestjs';

describe('MyService', () => {
  let smsService: SmsService;

  beforeEach(async () => {
    const module = await Test.createTestingModule({
      imports: [
        SmsModule.forTesting({
          shouldFail: false, // Control if SMS should succeed/fail
          delay: 100,        // Simulate network delay
        }),
      ],
      providers: [MyService],
    }).compile();

    smsService = module.get<SmsService>(SmsService);
  });

  it('should send SMS successfully', async () => {
    const response = await smsService.verify({
      to: '+989123456789',
      template: 'test-template',
      tokens: { code: '123456' },
    });

    expect(response.success).toBe(true);
    expect(response.messageId).toBeDefined();
  });
});

Mock Specific Scenarios

// Test failure scenarios
const failingModule = await Test.createTestingModule({
  imports: [
    SmsModule.forTesting({
      shouldFail: true,  // All SMS will fail
      delay: 0,
    }),
  ],
}).compile();

// Test with realistic delays
const realisticModule = await Test.createTestingModule({
  imports: [
    SmsModule.forTesting({
      shouldFail: false,
      delay: 500,  // 500ms delay to simulate real network
    }),
  ],
}).compile();

📊 API Reference

SmsService Methods

verify(message: ISmsMessage): Promise<ISmsResponse>

Send a verification SMS (OTP, etc.)

verifyBulk(messages: ISmsMessage[], options?): Promise<ISmsResponse[]>

Send multiple SMS messages with concurrency control

createVerificationMessage(to, template, tokens, options?): ISmsMessage

Create a properly formatted SMS message object

getAvailableDrivers(): DriverType[]

Get list of available/configured drivers

isDriverAvailable(driver: DriverType): boolean

Check if a specific driver is available

getDefaultDriver(): DriverType

Get the default driver configured

getServiceInfo(): ServiceInfo

Get comprehensive service information

healthCheck(): Promise<HealthStatus>

Perform service health check

Configuration Helpers

NestSmsConfigHelper.createFromConfigService(configService): ISmsConfig

Create configuration from NestJS ConfigService

NestSmsConfigHelper.createForTesting(options?): ISmsConfig

Create configuration for testing with mock driver

NestSmsConfigHelper.validateDriverEnvironment(driver, configService): boolean

Validate required environment variables for a driver

🔍 Troubleshooting

Common Issues

  1. Module not found errors

    npm install @mirad-work/sms-core
  2. Configuration validation errors

    // Check required environment variables
    const missing = NestSmsConfigHelper.getMissingEnvironmentVars(
      DriverType.KAVENEGAR, 
      configService
    );
    console.log('Missing vars:', missing);
  3. SMS not sending

    // Check service health
    const health = await smsService.healthCheck();
    console.log('Service status:', health);
       
    // Check available drivers
    const drivers = smsService.getAvailableDrivers();
    console.log('Available drivers:', drivers);

Debug Mode

Enable debug logging in your NestJS application:

// main.ts
import { Logger } from '@nestjs/common';

const app = await NestFactory.create(AppModule, {
  logger: ['error', 'warn', 'log', 'debug'], // Enable debug logs
});

🤝 Contributing

Contributions are welcome! Please read our Contributing Guide for details.

Development Setup

# Clone the repository
git clone https://github.com/mirad-work/sms-nestjs
cd sms-nestjs

# Install dependencies
npm install

# Run tests
npm test

# Run tests with coverage
npm run test:cov

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

🏢 About Mirad Work

This package is maintained by Mirad Work Organization - building quality open-source tools for the Persian/Iranian developer community.

📞 Support

🙏 Acknowledgments