Skip to main content
A dependency injection container centralises the wiring of your application. Instead of constructing services and passing them down through every function call, you register each service once and let the container resolve the full dependency graph on demand. The result is code that is easier to test (swap a real database for a mock by changing one registration), easier to reason about (all wiring lives in one place), and naturally decoupled (services depend on abstractions, not concrete classes). @figentra/container gives you a lightweight, fully-typed DI container purpose-built for Figentra monorepos.

Installation

Core Concepts

Registration

Registration is the act of binding a token — a string or symbol that identifies a dependency — to a factory function that produces an instance of it. The factory receives the container itself, so it can resolve its own dependencies.

Resolution

Resolution is the act of asking the container for the instance behind a token. The container calls the factory (honouring the configured scope) and returns the result, typed as the generic parameter you provide.

Scopes

The container supports two lifetimes:
  • Singleton — the factory runs once; every call to resolve returns the same instance. Use this for stateful services like database connections or caches.
  • Transient — the factory runs on every resolve call, producing a fresh instance each time. Use this for stateless utilities or request-scoped objects.

Quick Start

1

Create a container instance

Instantiate a single Container at your application root. In a monorepo, this is usually your app’s entry point (src/main.ts).
2

Register services

Register all your services before the application starts accepting requests. Factories receive the container so they can resolve sub-dependencies.
3

Resolve services at startup

Resolve the top-level service that roots the rest of your dependency graph. All transitive dependencies are resolved automatically.

API Reference

new Container()

Creates a new, empty container. The container has no pre-registered services; you build it from scratch.

container.register(token, factory, options?)

Registers a service with an explicit scope option. Defaults to transient scope if options is omitted.
string | symbol
required
The unique identifier for this service. Use a string for simplicity or a Symbol for guaranteed uniqueness across packages (see the tip below).
(container: Container) => T
required
A function that constructs and returns the service instance. The container passes itself as the first argument so you can resolve sub-dependencies inline.
{ scope: 'singleton' | 'transient' }
Controls the lifetime of the registered service. Defaults to { scope: 'transient' }.

container.resolve<T>(token)

Retrieves the service registered under token, typed as T. Throws a ContainerError if no service is registered for the given token.
string | symbol
required
The token used during registration.

container.registerSingleton(token, factory)

Shorthand for container.register(token, factory, { scope: 'singleton' }). The factory runs exactly once; subsequent calls to resolve return the cached instance.
string | symbol
required
The unique identifier for this service.
(container: Container) => T
required
A function that constructs the singleton instance.

Full Example

The following shows a complete application bootstrap: a database service, a cache service, and a user service that depends on both — all wired through the container.
Use TypeScript Symbol values as tokens instead of plain strings to guarantee uniqueness across packages and prevent accidental token collisions when two packages register a service with the same name.