Functional Instantiation
Most dependencies are resolved via constructor injection. But sometimes you need to create an instance on demand at runtime — inside an interceptor, conditionally based on request data, or for a class that isn't part of the controller's constructor.
useControllerContext().instantiate
The useControllerContext() composable provides an instantiate function that creates class instances through Moost's DI system, respecting scopes, provide registries, and replace registries:
import { Injectable, defineBeforeInterceptor, defineAfterInterceptor, useControllerContext } from 'moost'
@Injectable()
class MetricsCollector {
startTimer() { /* ... */ }
record() { /* ... */ }
}
const startMetrics = defineBeforeInterceptor(async (reply) => {
const { instantiate } = useControllerContext()
const metrics = await instantiate(MetricsCollector)
metrics.startTimer()
})
const recordMetrics = defineAfterInterceptor(async (response, reply) => {
const { instantiate } = useControllerContext()
const metrics = await instantiate(MetricsCollector)
metrics.record()
})instantiate(ClassName) returns a Promise<T> — it resolves the class through the same DI container used for constructor injection, scoped to the current controller instance.
What useControllerContext Returns
Beyond instantiate, the composable exposes the current controller's runtime context:
| Method | Returns |
|---|---|
instantiate(Class) | DI-resolved instance of the given class |
getController() | Current controller instance |
getMethod() | Current handler method name |
getRoute() | Full route path (prefix + handler path) |
getPrefix() | Controller's computed prefix (accumulated from all parents) |
getControllerMeta() | Class-level metadata |
getMethodMeta(name?) | Method-level metadata |
getPropMeta(name) | Property-level metadata (alias for getMethodMeta(name)) |
getScope() | Controller's injectable scope — true | 'SINGLETON' | 'FOR_EVENT' (plain @Controller()/@Injectable() classes yield true, which also means singleton — check scope === 'FOR_EVENT', not scope === 'SINGLETON') |
getParamsMeta() | Array of handler parameter metadata (always an array; empty when none) |
getPropertiesList() | Decorated property keys — (string | symbol)[] |
When to Use
- Interceptors that need a service not in the controller's constructor
- Conditional creation — instantiate a class only when a feature flag or request condition is met
- Dynamic dispatch — choose which class to instantiate based on runtime data
For most cases, constructor injection is simpler and more predictable. Use instantiate when static injection patterns don't fit.
Gotchas
- The target class must be DI-visible.
instantiate()rejects classes without@Injectable()(or a matching provide-registry entry) withClass is not Injectable and not Optional. - Requires an active event context.
useControllerContext()only works inside handlers and interceptors; calling it at module top level (or outside an event) throwsNo active event context.