NestJS — Enterprise Node.js

Custom Providers

16 min Lesson 8 of 48

Custom Providers

The shorthand providers: [UsersService] covers most cases, but NestJS offers four explicit provider definitions for full control over what gets injected for a given token. These are the key to flexible, testable, configurable applications.

The full provider object

Every provider can be written as an object with a provide token and one of four strategies: useClass, useValue, useFactory, or useExisting.

useClass — swap the implementation

Bind a token to a class, and you can change which class is used without touching the consumers. This is perfect for swapping implementations per environment:

const loggerProvider = { provide: LoggerService, useClass: process.env.NODE_ENV === 'production' ? ProductionLogger : DevelopmentLogger, }; @Module({ providers: [loggerProvider] }) export class AppModule {}

Anything that injects LoggerService gets the right logger for the environment — no consumer code changes.

useValue — inject a constant

Provide a ready-made value: a config object, a constant, or a mock in tests:

const configProvider = { provide: 'APP_CONFIG', useValue: { apiUrl: 'https://api.example.com', timeout: 5000 }, };
useValue shines in testing. Replace a real provider with a mock object in your test module and the rest of the app is none the wiser — no network calls, fully deterministic.

useFactory — build it dynamically

When a provider needs computation or its own dependencies to be created, use a factory function. List its dependencies in inject and they are passed as arguments:

const dbProvider = { provide: 'DATABASE_CONNECTION', useFactory: (config: ConfigService) => { return createConnection({ url: config.get('DB_URL') }); }, inject: [ConfigService], // injected into the factory };

Factories can be async too — return a Promise and NestJS waits for it before the app finishes booting.

useExisting — create an alias

useExisting points one token at another, creating an alias to the same instance (not a copy):

const aliasProvider = { provide: 'AliasedLogger', useExisting: LoggerService, // same singleton, two names };
useExisting reuses the existing instance; useClass creates a new one. Confusing the two leads to two separate instances when you expected one shared object — a subtle source of state bugs.

Choosing the right one

  • useClass — when the implementation should vary (per environment, per feature).
  • useValue — for constants, config objects, and test mocks.
  • useFactory — when creation needs logic, async work, or other providers.
  • useExisting — to expose one provider under an additional token.

Summary

Custom providers decouple a token from its implementation. useClass swaps implementations, useValue injects constants and mocks, useFactory builds providers dynamically (with their own injected dependencies), and useExisting aliases an existing instance. Next we make modules themselves configurable with dynamic modules and provider scopes.