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_URL is the token.
  • string is 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

  • InjectionToken provides 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, and multi providers.
  • ✔ Can define default factories.
  • ✔ Supports both @Inject() and inject().
  • ✔ Avoid string-based provider tokens.
  • ✔ Group related configuration into a single token.
  • InjectionToken is a must-know Angular interview topic.

Next Article

➡️ 35-Services-in-Angular-QA.md