Real World Example

Switching SMS Providers Without Code Changes

Indian apps often switch SMS vendors (MSG91, Twilio, Gupshup) for cost or delivery rates. With a token and useFactory, the vendor is chosen from config.

src/notifications/sms.module.ts

import { Module } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Msg91Sender } from './msg91.sender';
import { TwilioSender } from './twilio.sender';
import { ConsoleSender } from './console.sender';

export interface SmsSender { send(phone: string, text: string): Promise<void>; }
export const SMS_SENDER = Symbol('SMS_SENDER');

@Module({
  providers: [
    Msg91Sender,
    TwilioSender,
    ConsoleSender,
    {
      provide: SMS_SENDER,
      inject: [ConfigService, Msg91Sender, TwilioSender, ConsoleSender],
      useFactory: (config: ConfigService, msg91: Msg91Sender, twilio: TwilioSender, dev: ConsoleSender): SmsSender => {
        switch (config.get('SMS_VENDOR')) {
          case 'msg91': return msg91;
          case 'twilio': return twilio;
          default: return dev;          // local development: print to console
        }
      },
    },
  ],
  exports: [SMS_SENDER],
})
export class SmsModule {}
@Injectable()
export class OtpService {
  constructor(@Inject(SMS_SENDER) private readonly sms: SmsSender) {}

  async sendOtp(phone: string, otp: string) {
    await this.sms.send(phone, `${otp} is your ShopEase OTP. Valid for 5 minutes.`);
  }
}