Use multiple accounts
TwilioModule.forRoot() configures the default client and the options every
named client inherits. TwilioModule.registerClient() adds further clients
(typically Twilio subaccounts) which inherit those options and override only
what differs.
This mirrors BullModule.forRoot() / BullModule.registerQueue() in
@nestjs/bullmq, where the root registration holds shared configuration and
each named registration inherits it.
Basic usage
Section titled “Basic usage”import { Module } from '@nestjs/common';import { TwilioModule } from 'nestjs-twilio';
@Module({ imports: [ TwilioModule.forRoot({ accountSid: process.env.TWILIO_ACCOUNT_SID, authToken: process.env.TWILIO_AUTH_TOKEN, region: 'ie1', edge: 'dublin', }), TwilioModule.registerClient({ name: 'billing', accountSid: process.env.TWILIO_BILLING_ACCOUNT_SID, authToken: process.env.TWILIO_BILLING_AUTH_TOKEN, }), ],})export class AppModule {}The billing client uses its own credentials but inherits region: 'ie1' and
edge: 'dublin' from the root registration.
Injecting a named client
Section titled “Injecting a named client”import { Injectable } from '@nestjs/common';import { InjectTwilio, type TwilioClient } from 'nestjs-twilio';
@Injectable()export class BillingService { constructor(@InjectTwilio('billing') private readonly twilio: TwilioClient) {}}Names are matched case-insensitively, so 'Billing' and 'billing' resolve to
the same client. @InjectTwilio() with no argument resolves the default client
from forRoot().
To resolve the token directly (when wiring a provider by hand, for example),
use getTwilioClientToken('billing').
What is inherited
Section titled “What is inherited”Every option from forRoot() is inherited unless the named client overrides it
with a defined value.
| Registration | Result |
|---|---|
| Key omitted | Inherited from forRoot() |
Key set to undefined |
Inherited from forRoot() |
| Key set to a value | Overrides the inherited value |
// Inherits region 'ie1' even though the key is present.TwilioModule.registerClient({ name: 'billing', accountSid, authToken, region: process.env.BILLING_REGION, // undefined when unset});
// Deliberately overrides, routing this client through Australia.TwilioModule.registerClient({ name: 'au', accountSid, authToken, region: 'au1' });Credentials inherit as a unit
Section titled “Credentials inherit as a unit”authToken and apiKey / apiSecret are alternative authentication
modes, not independent settings, so they are inherited together. A client
that supplies either mode drops the inherited other one:
TwilioModule.forRoot({ accountSid, authToken });
// Authenticates with the API key. The root's authToken is dropped, not// merged alongside it.TwilioModule.registerClient({ name: 'realtime', apiKey, apiSecret });Merging them field-by-field would let the inherited authToken outrank the
explicitly configured apiKey, making it impossible to register an API-key
client (the kind Access Tokens require) beneath
an auth-token root.
Everything that is not a credential, including region and edge, keeps
inheriting across a mode change.
Asynchronous registration
Section titled “Asynchronous registration”registerClientAsync() accepts the same shape as Nest’s own generated *Async
methods. Use exactly one of useFactory, useClass or useExisting.
TwilioModule.registerClientAsync({ name: 'billing', imports: [ConfigModule], useFactory: (config: ConfigService) => ({ accountSid: config.getOrThrow('TWILIO_BILLING_ACCOUNT_SID'), authToken: config.getOrThrow('TWILIO_BILLING_AUTH_TOKEN'), }), inject: [ConfigService],});With a factory class, implement TwilioClientOptionsFactory:
import { Injectable } from '@nestjs/common';import type { TwilioClientOptionsFactory, TwilioModuleOptions } from 'nestjs-twilio';
@Injectable()export class BillingTwilioConfig implements TwilioClientOptionsFactory { constructor(private readonly config: ConfigService) {}
createTwilioClientOptions(): Partial<TwilioModuleOptions> { return { accountSid: this.config.getOrThrow('TWILIO_BILLING_ACCOUNT_SID'), authToken: this.config.getOrThrow('TWILIO_BILLING_AUTH_TOKEN'), }; }}TwilioModule.registerClientAsync({ name: 'billing', useClass: BillingTwilioConfig });Options resolved asynchronously inherit from forRoot() exactly as synchronous
ones do.
Without a root registration
Section titled “Without a root registration”registerClient() works on its own, which suits a platform where every tenant
is a subaccount and there is no meaningful primary account. Each registration
must then carry full credentials, since there is nothing to inherit.
@Module({ imports: [ TwilioModule.registerClient({ name: 'tenant-a', accountSid: '…', authToken: '…' }), TwilioModule.registerClient({ name: 'tenant-b', accountSid: '…', authToken: '…' }), ],})export class AppModule {}Global registration
Section titled “Global registration”forRoot() always registers globally, as TypeOrmCoreModule, Mongoose’s core
module and BullModule.forRoot() all do. Import it once in your root module
and every client is available application-wide.
This is also what makes inheritance work: a named client is registered as its own module, and a non-global root would be invisible to it.
Reference
Section titled “Reference”See TwilioModule for the full method
signatures, getTwilioClientToken
for the DI token helper, and InjectTwilio
for the injection decorator.