Skip to content

SMS

Transactional SMS sending with pluggable transport backends. The contract provides a concrete SmsService that applies config defaults (from) and delegates to an abstract SmsTransport sub-contract — the same pattern as mail (MailService + MailTransport).

Contract (@modularityjs/sms)

typescript
class SmsService {
  send(message: SmsMessage): Promise<SmsSendResult>;
}

abstract class SmsTransport {
  abstract send(message: SmsMessage): Promise<SmsSendResult>;
}

SmsService is concrete — it composes config defaults and delegates to SmsTransport. Drivers implement SmsTransport.

Setup

typescript
import { SmsModule } from '@modularityjs/sms';
import { SmsTwilioModule } from '@modularityjs/sms-twilio';

const modules = [
  SmsModule.forRoot({ from: '+15551234567' }),
  SmsTwilioModule.forRoot({
    accountSid: 'ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
    authToken: 'your_auth_token',
  }),
];

Config

FieldTypeDefaultDescription
fromstringDefault sender number

Per-message from overrides the config default.

Drivers

Twilio (@modularityjs/sms-twilio)

SMS delivery via Twilio. Sends messages through the Twilio REST API.

typescript
SmsTwilioModule.forRoot({
  accountSid: 'ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
  authToken: 'your_auth_token',
});
FieldTypeDefaultDescription
accountSidstringTwilio Account SID
authTokenstringTwilio Auth Token

Memory (@modularityjs/sms-memory)

In-memory transport for testing. Stores sent messages in an array with getSent() and clear().

typescript
import { SmsMemoryModule, MemorySmsTransport } from '@modularityjs/sms-memory';

const modules = [SmsModule.forRoot({ from: '+15551234567' }), SmsMemoryModule];

// In tests — get the transport from the container
const transport = container.get(MemorySmsTransport);
expect(transport.getSent()).toHaveLength(1);
transport.clear();

Sending SMS

Simple message

typescript
await smsService.send({
  to: '+15559876543',
  body: 'Your verification code is 123456.',
});

Multiple recipients

typescript
await smsService.send({
  to: ['+15559876543', '+15551112222'],
  body: 'Server maintenance scheduled for tonight at 10 PM.',
});

Queue Bridge (@modularityjs/sms-queue)

Async SMS delivery via the queue system. Publishes SmsMessage to a queue topic instead of sending synchronously. Follows the same pattern as @modularityjs/mail-queue.

typescript
import { SmsQueueModule, SMS_SEND_TOPIC } from '@modularityjs/sms-queue';

Web process — enqueues SMS

typescript
const modules = [
  RedisModule.forRoot({ host: 'localhost', port: 6379 }),
  SmsModule.forRoot({ from: '+15551234567' }),
  SmsQueueModule, // overrides SmsTransport with queue publisher
  QueueModule,
  QueueRedisModule,
];

Worker process — sends via Twilio

typescript
const modules = [
  RedisModule.forRoot({ host: 'localhost', port: 6379 }),
  SmsModule.forRoot({ from: '+15551234567' }),
  SmsTwilioModule.forRoot({
    accountSid: 'ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
    authToken: 'your_auth_token',
  }),
  QueueModule,
  QueueRedisModule,
  AppSmsConsumerModule, // your @Consume({ topic: 'sms.send', name: 'sms-send-handler' }) handler
];

The consumer listens to SMS_SEND_TOPIC ('sms.send') and sends via the real SmsService (which resolves to Twilio in the worker process).

Custom Transport

Implement SmsTransport for any backend:

typescript
@Injectable()
class VonageSmsTransport extends SmsTransport {
  async send(message: SmsMessage): Promise<SmsSendResult> {
    // Use Vonage SMS API directly
    const result = await vonage.sms.send({ ... });
    return { messageId: result.messageId, accepted: [...], rejected: [] };
  }
}

@Module({
  name: 'sms-vonage',
  imports: [SmsModule],
  providers: [VonageSmsTransport],
  preferences: [{ provide: SmsTransport, useClass: VonageSmsTransport }],
})
class SmsVonageModule {}

Notification Integration

SMS works as a notification channel via @modularityjs/notification-sms. See the Notification docs for setup and usage.