Skip to content

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:

ts
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:

MethodReturns
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) with Class 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) throws No active event context.

Released under the MIT License.