Module reference
ModuleRef exposes the underlying injector so you can look up providers outside of constructor injection. Use it sparingly for
dynamic workflows, plug-in systems, or bridging circular dependencies.
Resolving providers at runtime
Call moduleRef.resolve(Token) to retrieve a provider using the same injection graph Nael builds at bootstrap. The promise resolves
once the provider is ready; if it was request-scoped, it will honor the current context.
import { Injectable } from '@nl-framework/core';import { ModuleRef } from '@nl-framework/core';@Injectable()export class ReportsService { constructor(private readonly moduleRef: ModuleRef) {} async sendWeeklyReport() { const email = await this.moduleRef.resolve(EmailService); await email.send('ops@example.com', 'Weekly status', '...'); }}Controlling lookup boundaries
By default, resolve() searches the entire module graph. Pass strict: true to restrict the lookup to the current module
(and its providers). This is useful for enforcing encapsulation.
const cache = await this.moduleRef.resolve(CacheService, { strict: true,});// Throws if CacheService is not part of the current module tree.Creating new instances
Use moduleRef.create() when you need a throwaway instance that should not be cached by the container. Nael will construct the
provider and resolve its dependencies, but ownership stays with the caller.
const exporter = await this.moduleRef.create(ExporterService);await exporter.export();// create() bypasses the shared singleton and gives you a fresh instance.Accessing module-scoped providers
When you need to traverse module boundaries manually, moduleRef.get() accepts strings, class tokens, or symbols. Combine it with
strict: false to reach exported providers from imported modules.
const billingModule = this.moduleRef.get('BillingModule', { strict: false });const billingService = billingModule.get(BillingService);Prefer constructor injection or explicit exports whenever possible; ModuleRef is a powerful escape hatch but can reduce clarity if overused.