Module Internals — Series 2

Preview — 3 of 10 questions

What capability does DiscoveryModule provide that plain DI does not?

javascript
@Injectable()
export class HandlerRegistry implements OnModuleInit {
  constructor(
    private readonly discovery: DiscoveryService,
    private readonly scanner: MetadataScanner,
    private readonly reflector: Reflector,
  ) {}

  onModuleInit() {
    for (const wrapper of this.discovery.getProviders()) {
      const { instance } = wrapper;
      if (!instance || typeof instance !== 'object') continue;
      this.scanner.getAllMethodNames(Object.getPrototypeOf(instance))
        .forEach(name => {
          const meta = this.reflector.get('EVENT_HANDLER', instance[name]);
          if (meta) this.register(meta, instance, name);
        });
    }
  }
}
AIt replaces the DI container with a service-locator pattern, so providers no longer need to be registered in modules
BIt exposes hidden framework-internal providers that are otherwise unreachable by application code
CIt compiles decorator metadata ahead of time so Reflector lookups become synchronous
DIt lets a provider enumerate every provider and controller instance in the container at runtime, so decorator metadata can be scanned to build a registry without each class explicitly registering itself

Given @SetMetadata('roles', ['admin']) on the class and @SetMetadata('roles', ['editor']) on the handler, and equivalent tags metadata, what do the two calls return?

javascript
const roles = this.reflector.getAllAndOverride<string[]>('roles', [
  context.getHandler(),
  context.getClass(),
]);

const tags = this.reflector.getAllAndMerge<string[]>('tags', [
  context.getHandler(),
  context.getClass(),
]);
Aroles is ['admin', 'editor'] and tags is ['editor'] — the two helpers behave identically apart from ordering
BBoth throw, because metadata defined at two levels is ambiguous
Croles is ['editor'] — the first target in the array that has the key wins — while tags is ['editor', 'admin'], concatenating every level
Droles is ['admin'], because class metadata always takes precedence over handler metadata

Both calls pass structurally identical options. How does Nest decide whether these are one module instance or two?

javascript
@Module({ imports: [ThrottlerModule.forRoot({ ttl: 60, limit: 10 })] })
export class CatsModule {}

@Module({ imports: [ThrottlerModule.forRoot({ ttl: 60, limit: 10 })] })
export class DogsModule {}
ADynamic modules are always instantiated once per application, regardless of the options passed
BNest computes a module token from the module class plus a hash of the returned dynamic metadata, so two forRoot() calls producing equivalent metadata resolve to the same cached module instance — differing options would produce distinct tokens and distinct instances
CEach forRoot() call always produces a brand-new module instance, because the returned object is a fresh reference every time
DThe decision is made by the order of the imports arrays — the first registration wins and later ones are discarded silently

Sign up free to play

Answer all 10 questions (7 more), see explanations for every answer, and track your score.