Dependency injection, providers, and application boundaries
Angular dependency injection supplies services according to a hierarchical provider tree.
inject and tokens
export const API_BASE_URL = new InjectionToken<string>('API_BASE_URL');
@Injectable({ providedIn: 'root' })
export class OrdersApi {
private http = inject(HttpClient);
private baseUrl = inject(API_BASE_URL);
}Use class tokens for stable services and InjectionToken
for configuration, interfaces, factories, and multi-provider extension
points.
Provider scope
Root providers are shared for the application lifetime. Route providers create a feature-scoped instance. Component providers create a subtree instance. Scope is architecture: it decides state sharing, cleanup, and test boundaries.
Provider forms
useClass, useValue,
useFactory, and useExisting express
construction or aliasing. Multi providers collect extensions such as
interceptors.
Injection context
inject() works in an injection context—constructors,
field initializers, provider factories, and APIs that establish one. It
is not a global service locator callable anywhere.
Environment configuration
Compile-time environment replacement is not enough for one immutable image across environments. Consider loading validated runtime configuration before bootstrap or serving a configuration endpoint.
Avoid god services
A service should own a coherent responsibility: HTTP adapter, application workflow, state store, analytics port, or browser capability. Do not put every feature's state into one root singleton.
Feynman check
The injector is a tree of cupboards. Angular looks in the nearest cupboard and then walks upward. Explain why the same service token can yield different instances on two routes.