Runtime Values

Pass Request Data Into the Graph

Some classes need values that are only known at runtime: user IDs, tenant IDs, request IDs, roles, config strings, or feature flags. Use Parameter tokens for those values.

Defining a Parameter Token

Call Parameter.create("name") to create a token. The name is for debugging purposes — the actual token identity is the returned object reference.

tokens.ts
export const USER_ID = Parameter.create("userId");
export const USER_ROLE = Parameter.create("userRole");

Injecting a Parameter

Use @Inject(TOKEN) exactly like any other dependency. The parameter token is a first-class container citizen.

current_user.ts
@Component({ scope: LoadAs.Scoped })
export class CurrentUser {
  constructor(
    @Inject(USER_ID) readonly userId: string,
    @Inject(USER_ROLE) readonly role: "admin" | "member",
  ) {}
}

Supplying Values at Resolution Time

Pass values when you call container.instance() using TOKEN.value(someValue). The container uses the value for that resolution and all of its transitive dependencies.

resolve.ts
const reader = await requestScope.instance(PermissionReader, [
  USER_ID.value("user-123"),
  USER_ROLE.value("admin"),
]);

Why Scoped Matters

A singleton is created once and reused. If a singleton consumes a runtime parameter, it can accidentally keep the first value it received. Request-specific values belong in scoped state so each request, page, or conversation gets its own instance.

Rule of thumb: If a class consumes a Parameter whose value changes per request, make that class LoadAs.Scoped or LoadAs.Transient.

Full Example

Here is the complete pattern — token definition, injection, scoped container setup, and resolution — all together:

permissions.ts
import createContainer, {
  Component,
  Inject,
  LoadAs,
  Parameter,
} from "@noego/ioc";

export const USER_ID = Parameter.create("userId");
export const USER_ROLE = Parameter.create("userRole");

@Component({ scope: LoadAs.Scoped })
export class CurrentUser {
  constructor(
    @Inject(USER_ID) readonly userId: string,
    @Inject(USER_ROLE) readonly role: "admin" | "member",
  ) {}
}

@Component({ scope: LoadAs.Scoped })
export class PermissionReader {
  constructor(
    @Inject(CurrentUser) private user: CurrentUser,
  ) {}

  canEdit(): boolean {
    return this.user.role === "admin";
  }
}

const root = createContainer();
const requestScope = root.extend();

const reader = await requestScope.instance(PermissionReader, [
  USER_ID.value("user-123"),
  USER_ROLE.value("admin"),
]);

Rules

  • Create one Parameter token per runtime value.
  • Parameter tokens are plain values — they don't need @Component.
  • Pass parameter values at the top-level resolution call using .value().
  • Keep parameter-consuming classes Scoped or Transient when values change per request.
  • Do not use parameters for dependencies that should be normal injected services.
NoEgo

© 2025 NoEgo. All rights reserved.