Dependency Injection
Decorator-based DI with singleton/transient scopes, token-based injection, and per-instance scoped containers.
npm install @praxisjs/dipnpm add @praxisjs/diyarn add @praxisjs/dibun add @praxisjs/di@Injectable(options?)
Registers a class in the global container. Must be applied for the class to be resolvable.
import { Injectable } from '@praxisjs/di'
@Injectable()
class LoggerService {
log(msg: string) { console.log('[LOG]', msg) }
}
@Injectable({ scope: 'transient' }) // new instance on every resolve
class RequestContext { /* ... */ }Default scope is 'singleton' — one instance per container.
@Inject(dep)
Lazily injects a dependency into a field. Resolves on first access from the container (or scoped container if inside @Scope).
@Injectable()
class UserService {
@Inject(LoggerService) private logger!: LoggerService
greet(name: string) {
this.logger.log(`Hello, ${name}`)
}
}Works in any class — not limited to components.
@InjectContainer()
Injects the container itself. Inside a @Scope class, injects the scoped child container.
@Injectable()
class ServiceLocator {
@InjectContainer() private container!: Container
get<T>(token: Token<T>): T {
return this.container.resolve(token)
}
}Token-based injection
For non-class dependencies (interfaces, primitives, config):
import { token, container } from '@praxisjs/di'
const CONFIG_TOKEN = token<AppConfig>('config')
container.registerValue(CONFIG_TOKEN, {
apiUrl: 'https://api.example.com',
timeout: 5000,
})
@Injectable()
class ApiService {
@Inject(CONFIG_TOKEN) private config!: AppConfig
}In components with @Inject
Use @Inject directly on component fields — no bridge function needed:
import { Inject } from '@praxisjs/di'
import { Component } from '@praxisjs/decorators'
import { StatefulComponent } from '@praxisjs/core'
@Component()
class Dashboard extends StatefulComponent {
@Inject(AnalyticsService) private analytics!: AnalyticsService
onMount() {
this.analytics.track('dashboard:view')
}
render() { return <div>Dashboard</div> }
}@Scope(configure?)
Creates a child container scoped to each instance of the class. @Inject and @InjectContainer inside the class resolve from this child container automatically.
import { Scope, Inject } from '@praxisjs/di'
@Scope((c) => {
c.register(UserRepository)
c.registerValue(AUTH_TOKEN, getCurrentToken())
})
@Component()
class UserModule extends StatefulComponent {
@Inject(UserRepository) private repo!: UserRepository
render() { /* ... */ }
}Each UserModule instance gets its own isolated container that inherits all global registrations. Useful for feature modules, dialogs, or multi-tenant layouts.
Circular dependency detection
When a circular dependency is detected, the container throws an error describing the full resolution chain:
Circular dependency detected: ServiceA → ServiceB → ServiceAThis prevents infinite loops during service resolution and makes the dependency cycle easy to identify and fix.
Container API
import { container } from '@praxisjs/di'
container.register(MyService)
container.register(MyService, { scope: 'transient' })
container.registerValue(TOKEN, value)
container.registerFactory(TOKEN, (c) => new MyService(c.resolve(Dep)))
container.resolve(MyService)
// Create a child container (inherits parent registrations)
const child = container.createChild()
child.registerValue(REQUEST_TOKEN, currentRequest)
child.resolve(MyService) // MyService can inject REQUEST_TOKEN