Angular InjectionToken
Learn Angular InjectionToken in detail with architecture diagrams, use cases, configuration injection, factory providers, multi providers, standalone applications, enterprise examples, best practices, and interview questions.
InjectionToken is one of the most important features of Angular's Dependency Injection (DI) system.
Angular normally injects dependencies using class types.
Example:
constructor(
private userService: UserService
) {}
However, not everything in Angular is a class.
Examples include:
- Configuration objects
- Environment settings
- API URLs
- Feature flags
- Interfaces
- Primitive values
- Arrays
- Factory-generated values
Since these do not exist as runtime class types, Angular needs another identifier.
That identifier is called an InjectionToken.
Understanding InjectionToken is essential for enterprise Angular development and is a common Angular interview topic.
Table of Contents
- What is InjectionToken?
- Why InjectionToken?
- How InjectionToken Works
- Creating InjectionTokens
- Injecting Configuration
- Injecting Primitive Values
- Factory Providers
- Multi Providers
- Standalone Applications
- Enterprise Examples
- Best Practices
- Top 10 Interview Questions
- Summary
What is InjectionToken?
An InjectionToken is a unique runtime identifier that Angular uses to locate a dependency when no class type is available.
Instead of using a class as the lookup key:
UserService
↓
Injector
↓
Service
Angular uses a token.
API_URL Token
↓
Injector
↓
Configuration Value
Dependency Injection Architecture
flowchart LR
Component
Component --> Injector
Injector --> InjectionToken
InjectionToken --> Provider
Provider --> Value
Value --> Component
Why Do We Need InjectionToken?
Classes exist at runtime.
class UserService {}
Interfaces do not.
interface PaymentService {}
After TypeScript compilation:
interface PaymentService
becomes
Nothing
Angular cannot inject something that no longer exists.
InjectionToken solves this problem.
InjectionToken Flow
sequenceDiagram
participant Component
participant Injector
participant Token
participant Provider
Component->>Injector: Request Token
Injector->>Provider: Find Matching Provider
Provider-->>Component: Return Dependency
Creating an InjectionToken
import { InjectionToken } from '@angular/core';
export const API_URL =
new InjectionToken<string>('API_URL');
Here:
API_URLis the token.stringis the type of the injected value.'API_URL'is a debugging description.
Registering a Provider
bootstrapApplication(AppComponent, {
providers: [
{
provide: API_URL,
useValue: 'https://api.company.com'
}
]
});
Injecting the Value
import {
Component,
Inject
} from '@angular/core';
@Component({
standalone: true,
template: `{{ apiUrl }}`
})
export class AppComponent {
constructor(
@Inject(API_URL)
private apiUrl: string
) {}
}
Output
https://api.company.com
Provider Resolution
flowchart TD
Component
Component --> Injector
Injector --> API_URL
API_URL --> Provider
Provider --> Value
Injecting Configuration Objects
Instead of injecting multiple primitive values, inject a configuration object.
export interface AppConfig {
apiUrl: string;
timeout: number;
production: boolean;
}
Create Token
export const APP_CONFIG =
new InjectionToken<AppConfig>(
'APP_CONFIG'
);
Register
providers:[
{
provide:APP_CONFIG,
useValue:{
apiUrl:'https://api.company.com',
timeout:30000,
production:true
}
}
]
Inject
constructor(
@Inject(APP_CONFIG)
private config:AppConfig
){}
Configuration Architecture
flowchart LR
AppConfig
AppConfig --> InjectionToken
InjectionToken --> Injector
Injector --> Component
Using inject()
Modern Angular supports inject().
import {
inject
} from '@angular/core';
@Component({
standalone:true,
template:''
})
export class HomeComponent{
config = inject(APP_CONFIG);
}
This avoids constructor injection when appropriate.
Injecting Primitive Values
export const APP_NAME =
new InjectionToken<string>('APP_NAME');
export const MAX_RETRY =
new InjectionToken<number>('MAX_RETRY');
export const ENABLE_CACHE =
new InjectionToken<boolean>('ENABLE_CACHE');
Providers
providers:[
{
provide:APP_NAME,
useValue:'Online Banking'
},
{
provide:MAX_RETRY,
useValue:5
},
{
provide:ENABLE_CACHE,
useValue:true
}
]
Primitive Value Architecture
flowchart TD
Token
Token --> String
Token --> Number
Token --> Boolean
String --> Component
Number --> Component
Boolean --> Component
Factory-Based InjectionToken
InjectionTokens can define a default factory.
export const API_VERSION =
new InjectionToken<string>(
'API_VERSION',
{
providedIn: 'root',
factory: () => 'v1'
}
);
Now Angular can provide the value even if no explicit provider is registered.
Factory Flow
flowchart LR
InjectionToken
InjectionToken --> Factory
Factory --> Value
Value --> Component
Multi Providers
InjectionTokens work with multi providers.
Example
export const PLUGINS =
new InjectionToken<string[]>(
'PLUGINS'
);
Register
providers:[
{
provide:PLUGINS,
useValue:'Logger',
multi:true
},
{
provide:PLUGINS,
useValue:'Security',
multi:true
},
{
provide:PLUGINS,
useValue:'Metrics',
multi:true
}
]
Inject
plugins =
inject(PLUGINS);
Result
[
'Logger',
'Security',
'Metrics'
]
Multi Provider Architecture
flowchart TD
InjectionToken
InjectionToken --> Logger
InjectionToken --> Security
InjectionToken --> Metrics
Logger --> Array
Security --> Array
Metrics --> Array
Factory Provider Example
export function appConfigFactory() {
return {
apiUrl:'https://api.company.com',
version:'v2'
};
}
providers:[
{
provide:APP_CONFIG,
useFactory:appConfigFactory
}
]
Useful when configuration depends on runtime values.
Standalone Application Example
bootstrapApplication(AppComponent,{
providers:[
{
provide:APP_CONFIG,
useValue:{
apiUrl:'https://api.company.com',
production:true
}
}
]
});
Standalone Angular applications commonly register InjectionTokens during bootstrap.
Enterprise Banking Example
Banking application configuration
export interface BankConfig{
apiUrl:string;
maxTransfer:number;
enableFraudCheck:boolean;
currency:string;
}
Token
export const BANK_CONFIG=
new InjectionToken<BankConfig>(
'BANK_CONFIG'
);
Provider
providers:[
{
provide:BANK_CONFIG,
useValue:{
apiUrl:'https://bank-api.company.com',
maxTransfer:100000,
enableFraudCheck:true,
currency:'USD'
}
}
]
Inject
config =
inject(BANK_CONFIG);
Enterprise Architecture
flowchart TD
App
App --> BANK_CONFIG
BANK_CONFIG --> Injector
Injector --> TransferService
Injector --> PaymentService
Injector --> LoanService
InjectionToken vs Class
| Class | InjectionToken |
|---|---|
| Runtime Type | Runtime Token |
| Represents a Class | Represents Any Dependency |
| Auto Injection | Requires Token |
| Used for Services | Used for Values, Interfaces, Configurations |
Common Use Cases
| Scenario | InjectionToken |
|---|---|
| API URL | ✅ |
| Environment Config | ✅ |
| Feature Flags | ✅ |
| Theme Configuration | ✅ |
| Logging Configuration | ✅ |
| Interfaces | ✅ |
| Primitive Values | ✅ |
| Arrays | ✅ |
Performance Best Practices
| Practice | Benefit |
|---|---|
| Use typed tokens | Compile-time safety |
| Use configuration objects | Fewer providers |
Prefer inject() in standalone APIs |
Cleaner code |
| Use factory functions for dynamic values | Flexible initialization |
| Group related settings | Easier maintenance |
| Give tokens descriptive names | Easier debugging |
Common Mistakes
Using Strings as Tokens
❌ Avoid
provide:'API_URL'
Prefer
provide:API_URL
where API_URL is an InjectionToken.
Creating Multiple Tokens for the Same Configuration
Keep one token per logical configuration object.
Using InjectionToken for Services
Normal services should typically be injected using their class types.
Use InjectionToken only when a runtime token is needed.
Forgetting @Inject()
Constructor injection with tokens requires:
constructor(
@Inject(APP_CONFIG)
private config:AppConfig
){}
Or use inject(APP_CONFIG).
Best Practices
- Use strongly typed
InjectionToken<T>. - Prefer configuration objects over many primitive values.
- Group related settings.
- Register tokens during bootstrap for standalone applications.
- Use factories for runtime configuration.
- Avoid string-based provider tokens.
- Use descriptive token names.
Advantages
| Feature | Benefit |
|---|---|
| Runtime Token | Supports non-class dependencies |
| Type Safe | Generic typing |
| Configuration Injection | Centralized settings |
| Factory Support | Dynamic values |
| Multi Provider Support | Extensible architecture |
| Standalone Ready | Modern Angular applications |
Top 10 Angular InjectionToken Interview Questions
1. What is an InjectionToken?
Answer
An InjectionToken is a runtime identifier used by Angular's Dependency Injection system to inject values, interfaces, configuration objects, arrays, or other dependencies that cannot be identified by a class type.
2. Why is InjectionToken needed?
Answer
Interfaces and primitive values do not exist as runtime types after TypeScript compilation. InjectionToken provides a unique runtime key for Angular to resolve these dependencies.
3. How do you create an InjectionToken?
Answer
export const API_URL =
new InjectionToken<string>(
'API_URL'
);
4. How do you inject an InjectionToken?
Answer
Using constructor injection:
constructor(
@Inject(API_URL)
private apiUrl:string
){}
or
const apiUrl =
inject(API_URL);
5. Can InjectionToken inject configuration objects?
Answer
Yes.
It is commonly used for application configuration.
6. Can InjectionToken work with factories?
Answer
Yes.
It supports both factory-based providers and default factories defined when the token is created.
7. Can InjectionToken be used with multi providers?
Answer
Yes.
Multiple values can be registered under the same token using multi: true.
8. What is the difference between a Service and an InjectionToken?
| Service | InjectionToken |
|---|---|
| Class-based dependency | Token-based dependency |
| Represents behavior | Represents values or abstractions |
| Automatically identified by class type | Requires an explicit token |
9. What are common use cases?
Answer
- API URLs
- Feature flags
- Environment configuration
- Theme settings
- Logging configuration
- Interface abstractions
- Arrays and plugin systems
10. What are the best practices for InjectionToken?
Answer
- Use strongly typed tokens.
- Prefer configuration objects over many primitive values.
- Avoid string tokens.
- Use
inject()in modern Angular where appropriate. - Use factory providers for runtime values.
- Use descriptive names.
Interview Cheat Sheet
| Topic | Remember |
|---|---|
| Purpose | Runtime DI token |
| Generic Type | InjectionToken<T> |
| Configuration | Best use case |
| Primitive Values | Supported |
| Interfaces | Supported |
| Factory | Supported |
| Multi Provider | Supported |
| Modern Injection | inject() |
| Constructor Injection | @Inject() |
| Avoid | String tokens |
Summary
InjectionToken is a powerful Angular Dependency Injection feature that enables developers to inject configuration objects, interfaces, primitive values, arrays, and dynamically created dependencies that cannot be represented by classes. It provides strong typing, clean architecture, and flexibility for enterprise applications. Combined with factory providers, multi providers, and standalone bootstrap APIs, InjectionToken is an essential tool for building scalable Angular applications.
Key Takeaways
- ✔
InjectionTokenprovides a runtime identifier for non-class dependencies. - ✔ Use
InjectionToken<T>for strong typing. - ✔ Perfect for configuration objects, feature flags, and API URLs.
- ✔ Supports primitive values and interfaces.
- ✔ Works with
useValue,useFactory, andmultiproviders. - ✔ Can define default factories.
- ✔ Supports both
@Inject()andinject(). - ✔ Avoid string-based provider tokens.
- ✔ Group related configuration into a single token.
- ✔
InjectionTokenis a must-know Angular interview topic.
Next Article
➡️ 35-Services-in-Angular-QA.md