Extension Points with the Module Reference API
Resolve scoped dependencies imperatively via ModuleRef to power third-party extensions.
Why ModuleRef Exists
NestJS resolves dependencies declaratively: you list providers in a constructor and the container wires them. But a plugin host can't know its extensions at compile time. You need to ask the container for a provider imperatively, at runtime.
ModuleRef is the DI container's public handle. It lets you:
- Retrieve an existing singleton by token (
get). - Resolve a scoped (REQUEST / TRANSIENT) instance on demand (
resolve). - Instantiate a class that was never registered as a provider (
create).
This is the foundation for plugin architectures and hexagonal extension points where adapters are chosen dynamically.
Injecting ModuleRef
ModuleRef is itself an injectable. Add it to any provider's constructor and Nest hands you the reference to the module instance that owns this provider.
Note that you cannot call get() in the constructor body if the dependency isn't ready yet — prefer onModuleInit for eager lookups so the whole module tree is constructed first.
import { Injectable, OnModuleInit } from '@nestjs/common';
import { ModuleRef } from '@nestjs/core';
import { AuditService } from './audit.service';
@Injectable()
export class PluginHost implements OnModuleInit {
private audit: AuditService;
constructor(private readonly moduleRef: ModuleRef) {}
onModuleInit() {
// Safe here: all providers are already instantiated.
this.audit = this.moduleRef.get(AuditService);
}
}All lessons in this course
- Ports and Adapters for Domain Isolation
- Dynamic Provider Registration with DiscoveryService
- Lazy-Loaded Modules and Feature Toggles
- Extension Points with the Module Reference API