install
source · Clone the upstream repo
git clone https://github.com/Intense-Visions/harness-engineering
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/Intense-Visions/harness-engineering "$T" && mkdir -p ~/.claude/skills && cp -r "$T/agents/skills/claude-code/nestjs-dependency-injection" ~/.claude/skills/intense-visions-harness-engineering-nestjs-dependency-injection-232ed4 && rm -rf "$T"
manifest:
agents/skills/claude-code/nestjs-dependency-injection/SKILL.mdsource content
NestJS Dependency Injection
Master NestJS DI container with tokens, useClass/useValue/useFactory providers
When to Use
- You need to provide an interface implementation that is swappable (e.g., a real database vs. an in-memory mock)
- You need to inject a plain value (config object, constant, SDK client instance) that is not a class
- You need to run async initialization before a provider is usable (factory providers with async/await)
- You want to understand why "Cannot resolve dependencies" errors occur
Instructions
-
Standard provider (shorthand):
— NestJS creates one singleton instance, injected by class type.providers: [MyService] -
useClass — swap implementations:
providers: [ { provide: MyService, useClass: process.env.NODE_ENV === 'test' ? MockMyService : MyService }, ];
- useValue — inject constants or SDK clients:
export const STRIPE_CLIENT = 'STRIPE_CLIENT'; providers: [ { provide: STRIPE_CLIENT, useValue: new Stripe(process.env.STRIPE_KEY!) } ] // inject with @Inject token constructor(@Inject(STRIPE_CLIENT) private stripe: Stripe) {}
- useFactory — async initialization:
providers: [ { provide: DATABASE_CONNECTION, useFactory: async (config: ConfigService): Promise<DataSource> => { const ds = new DataSource({ url: config.get('DATABASE_URL') }); await ds.initialize(); return ds; }, inject: [ConfigService], }, ];
- InjectionToken vs string tokens: Prefer
-based or class-based tokens over plain strings to avoid collisions:Symbol
export const MAIL_OPTIONS = new InjectionToken<MailOptions>('MailOptions');
- Inject in constructor with
when the token is not a class:@Inject(TOKEN)
constructor(@Inject(MAIL_OPTIONS) private options: MailOptions) {}
- Optional injection: Use
when a provider may not be registered:@Optional()
.constructor(@Optional() @Inject(CACHE) private cache?: Cache) {}
Details
NestJS uses a hierarchical IoC container built on top of Reflect metadata. When you add a class to
providers, the container reads its constructor parameter types via TypeScript's emitDecoratorMetadata and resolves each dependency recursively.
Token resolution: A provider token can be a class, a string, a Symbol, or an
InjectionToken<T>. The container matches the provide key to the @Inject() token (or the constructor type for class providers). Mismatched tokens are the second most common DI error after missing exports.
Scopes and singleton behavior: Default (singleton) scope means
useFactory runs once at app startup. REQUEST scope runs the factory (or constructor) per request — useful for per-request database connections or tenant-aware clients.
Testing:
overrideProvider(MyService).useValue(mockService) in Test.createTestingModule() replaces any token with a mock without touching the module graph. This is the cleanest way to unit-test controllers and services.
for dynamic resolution: When you need to resolve a provider at runtime (e.g., strategy pattern where the concrete implementation depends on runtime data), inject ModuleRef.resolve()
ModuleRef and call moduleRef.resolve(SomeService).
Source
https://docs.nestjs.com/fundamentals/custom-providers
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- Verify your implementation against the details and edge cases listed above.
Harness Integration
- Type: knowledge — this skill is a reference document, not a procedural workflow.
- No tools or state — consumed as context by other skills and agents.
Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.