Factory Providers

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.

4 min read Level 3/5 #nestjs#di#factory
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.

Async Providers →