Skip to content

@praxisjs/di

Bugs and broken features

This package may have bugs or partially broken functionality. If you run into something, feel free to open an issue or contribute on GitHub.

sh
npm install @praxisjs/di
sh
pnpm add @praxisjs/di
sh
yarn add @praxisjs/di

Dependency injection container with TypeScript decorators, token-based resolution, and scoped containers.

Basic Usage

ts
import { Injectable, Inject, container } from '@praxisjs/di'

@Injectable()
class LoggerService {
  log(msg: string) {
    console.log('[LOG]', msg)
  }
}

@Injectable()
class UserService {
  @Inject(LoggerService) private logger!: LoggerService

  greet(name: string) {
    this.logger.log(`Hello, ${name}`)
  }
}

const userService = container.resolve(UserService)
userService.greet('Alice')

@Injectable(options?)

Registers a class in the global container.

ts
@Injectable({ scope: 'singleton' })
class CacheService { /* ... */ }

@Injectable({ scope: 'transient' })
class RequestContext { /* ... */ }

Options:

OptionTypeDefaultDescription
scope'singleton' | 'transient''singleton'Singleton shares one instance; transient creates a new one on each resolve

@Inject<T>(dep)

Lazily injects a dependency into a class property. Resolves and caches on first access. In development, blocks direct property assignment to prevent bypassing the container.

ts
@Injectable()
class ApiClient {
  @Inject(AuthService) private auth!: AuthService
  @Inject(HTTP_CLIENT) private http!: HttpClient  // token injection

  async get(path: string) {
    return this.http.get(path, { token: this.auth.token })
  }
}

dep can be a class constructor or a Token<T>.


@InjectContainer()

Injects the DI container itself. Useful for dynamic resolution.

ts
@Injectable()
class ServiceLocator {
  @InjectContainer() private container!: Container

  resolve<T>(type: Constructor<T>): T {
    return this.container.resolve(type)
  }
}

Tokens

Use tokens for injecting values that aren't classes (primitives, config objects, interfaces).

token<T>(description)

Creates a typed injection token.

ts
import { token } from '@praxisjs/di'

const API_URL = token<string>('API_URL')
const HTTP_OPTIONS = token<HttpOptions>('HTTP_OPTIONS')

Registering token values

ts
container.registerValue(API_URL, 'https://api.example.com')
container.registerValue(HTTP_OPTIONS, { timeout: 5000 })

Injecting tokens

ts
@Injectable()
class ApiService {
  @Inject(API_URL) private baseUrl!: string
}

Container

The global container instance is available as a named export. You can also create child containers for isolated scopes.

container.register(target, options?)

Manually register a class.

ts
container.register(MyService, { scope: 'transient' })

container.registerValue(token, value)

Register a plain value.

ts
container.registerValue(API_URL, 'https://api.example.com')

container.registerFactory(token, factory)

Register a factory function that receives the container and returns the value.

ts
container.registerFactory(LOGGER, (c) => {
  const config = c.resolve(AppConfig)
  return config.debug ? new DebugLogger() : new SilentLogger()
})

container.resolve(target)

Resolves a class or token from the container.

ts
const service = container.resolve(UserService)
const url = container.resolve(API_URL)

container.createChild()

Creates a child container that inherits all registrations from its parent but has its own scope for new registrations.

ts
const requestScope = container.createChild()
requestScope.registerValue(REQUEST_ID, generateId())

createScope()

Shorthand for creating a scoped child container. Useful for request-level or component-level isolation.

ts
import { createScope } from '@praxisjs/di'

const scope = createScope()
scope.registerValue(SESSION, session)
const handler = scope.resolve(RequestHandler)

useService<T>(ServiceClass)

Composable to resolve a service from the global container inside a component.

ts
import { useService } from '@praxisjs/di'

@Component()
class Dashboard extends BaseComponent {
  analytics = useService(AnalyticsService)

  render() {
    return <div onMount={() => this.analytics.track('view')} />
  }
}

Or with a functional component:

ts
import { useService } from '@praxisjs/di'

function Dashboard() {
  const analytics = useService(AnalyticsService)

  return <div onMount={() => analytics.track('view')} />
}

Released under the MIT License.