Dynamic Provider Registration with DiscoveryService
Scan and wire providers at runtime using DiscoveryService and MetadataScanner for plugin systems.
The Plugin Wiring Problem
In a plugin architecture, you don't know at compile time which handlers, strategies, or adapters will exist. A hexagonal core defines ports; plugins supply adapters. The challenge: how does the framework find and wire those adapters without you hand-registering each one?
- Hard-coded arrays in a module are brittle — every new plugin edits core code.
- You want providers to self-declare their role via decorators, then be discovered at runtime.
NestJS ships @nestjs/core's DiscoveryService and MetadataScanner exactly for this. They let you scan the live DI container and react to metadata.
Marking Providers with a Decorator
The pattern starts with a custom decorator that stamps metadata onto a class. We use SetMetadata (or Reflector.createDecorator) so the scanner can later filter for it.
Here a @Plugin() decorator tags a class as a discoverable plugin and carries a name so the registry can key on it.
import { SetMetadata } from '@nestjs/common';
export const PLUGIN_KEY = 'app:plugin';
export interface PluginMeta {
name: string;
}
export const Plugin = (meta: PluginMeta): ClassDecorator =>
SetMetadata(PLUGIN_KEY, meta);
// A plugin author writes:
@Plugin({ name: 'csv-exporter' })
export class CsvExporter {
export(rows: unknown[]): string {
return rows.map((r) => JSON.stringify(r)).join('\n');
}
}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