> ## Documentation Index
> Fetch the complete documentation index at: https://docs.wyrly.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Lifetimes and scopes

> How singleton, scoped, and transient dependencies behave in Wyrly DI.

Lifetimes define how long resolved instances live.

Choosing the right lifetime keeps state ownership clear and prevents request-specific dependencies
from leaking across users or requests.

## Singleton

Use `singleton` for stateless services or process-wide dependencies.

```ts theme={null}
container.register(LoggerToken, {
  useClass: ConsoleLogger,
  lifetime: "singleton",
});
```

Good candidates:

* loggers
* configuration readers
* pure policies
* stateless mappers

Avoid putting request state in singletons.

## Scoped

Use `scoped` for dependencies that should be reused within one request or unit of work.

```ts theme={null}
container.register(UserRepositoryToken, {
  useClass: UserRepository,
  lifetime: "scoped",
});
```

Good candidates:

* request context
* repositories bound to a request transaction
* DataLoader instances
* request-local caches

## Transient

Use `transient` when every resolution should create a fresh instance.

```ts theme={null}
container.register(ReportBuilder);
```

Transient is useful for short-lived objects with no shared state requirement.

## Request scopes

In web frameworks, a request scope is created when the request starts and disposed when the request
finishes.

```ts theme={null}
const scope = container.createScope();

try {
  await handleRequest(scope);
} finally {
  await scope.dispose();
}
```

Framework adapters do this for you where possible.

## Child scopes

v2.1+ adds `scope.createChildScope()` for nested units of work inside one request (for example a
GraphQL subscription operation or a sub-task that should share parent scoped services).

```ts theme={null}
const requestScope = container.createScope();
const operationScope = requestScope.createChildScope();

try {
  const shared = requestScope.resolve(RequestDbToken);
  const local = operationScope.resolve(OperationContextToken);
  // operationScope reads parent scoped instances; new scoped instances stay on the child
} finally {
  await operationScope.dispose();
  await requestScope.dispose();
}
```

Rules:

* Dispose **child scopes before** the parent (`ScopeHasActiveChildrenError` otherwise).
* Child scopes can read parent `set()` values and parent scoped instances.
* New scoped instances created in a child are stored on that child only.

## Lifetime validation

Wyrly can validate lifetime relationships. For example, a singleton should not depend on a scoped
dependency — **directly or transitively** — because that would capture request-specific state in a
process-wide instance.

Validation also warns when `register()` overrides `@Injectable` `deps` or `lifetime` metadata.

Run validation as part of test or CI:

```ts theme={null}
const result = container.validate();
```
