Compute a Dependency at Runtime
Factory Providers
useFactory runs a function with injected dependencies and registers its return value as the provider — perfect for client SDKs and config-driven wiring.
What you'll learn
- Wire a provider with useFactory
- Inject other providers into the factory
- Combine factories with custom string tokens
useValue is great for static data, but real apps usually need to build
the dependency — read config, call a constructor with arguments, pick an
implementation based on env. That’s the job of useFactory.
A Minimal Factory
A factory is a function. Whatever it returns becomes the provider’s value.
@Module({
providers: [
{
provide: 'CLOCK',
useFactory: () => ({ now: () => new Date() }),
},
],
exports: ['CLOCK'],
})
export class ClockModule {} Anywhere you inject 'CLOCK', you get the same object the factory produced
on bootstrap.
Injecting Into a Factory
A factory’s superpower is asking for its own dependencies via the
inject array. The order in inject matches the order of the function
parameters.
@Module({
imports: [ConfigModule],
providers: [
{
provide: 'DB_CLIENT',
useFactory: (cfg: ConfigService) => {
return createClient(cfg.get('DB_URL'));
},
inject: [ConfigService],
},
],
exports: ['DB_CLIENT'],
})
export class DatabaseModule {} Nest builds ConfigService first, hands it to the factory, and registers
the returned client under the token 'DB_CLIENT'.
Picking an Implementation From Config
Factories shine when the right implementation depends on runtime state.
{
provide: MailerService,
useFactory: (cfg: ConfigService) => {
return cfg.get('NODE_ENV') === 'production'
? new SesMailer(cfg.get('AWS_REGION'))
: new ConsoleMailer();
},
inject: [ConfigService],
} Consumers inject MailerService everywhere; only the module knows which
implementation is in play.
Class Tokens or String Tokens?
You can provide: a class reference or a string/symbol token. Use the
class when you have one — it lets consumers inject by type, no @Inject
decorator needed.
// Class token — consumers inject `RedisClient` directly
{
provide: RedisClient,
useFactory: (cfg: ConfigService) => new RedisClient(cfg.get('REDIS_URL')),
inject: [ConfigService],
} For raw values (booleans, strings, plain objects) you need a string or symbol
token, and consumers use @Inject('TOKEN'). Symbols avoid name collisions in
larger codebases.