APP_INITIALIZER Migration to provideAppInitializer in Angular 19
Problem: Error using provideAppInitializer after Angular 19 upgrade
When migrating from Angular 18 to Angular 19, you may encounter an error when attempting to convert APP_INITIALIZER
implementations to the new provideAppInitializer
syntax. The automatic migration from ng update
transforms your code like this:
// Angular 18 approach
providers: [
{
provide: APP_INITIALIZER,
useFactory: initializeApp1,
deps: [AuthService],
multi: true
},
]
// Post-migration Angular 19 code
providers: [
provideAppInitializer(initializeApp1(inject(AuthService)))
]
After this change, you encounter a runtime error:
Uncaught RuntimeError: NG0203: inject() must be called from an injection context such as a constructor, a factory function, a field initializer, or a function used with
runInInjectionContext
This occurs because inject()
calls require an Angular injection context. The migration placed the dependency injection call (inject(AuthService)
) outside a suitable context.
Solution: Proper Wrapping Techniques
Preferred Approach: Wrap with Arrow Function
Best Practice
Wrap your initialization code in an arrow function to create the required injection context:
providers: [
provideAppInitializer(() => initializeApp1(inject(AuthService)))
]
Why this works:
- The arrow function (
() => ...
) creates a closure - Angular executes this closure within an injection context
inject(AuthService)
is safely called inside this context- The original factory function (
initializeApp1
) is preserved
Alternative: Directly Embed Initialization Logic
You can move dependencies inside the wrapper function for simpler cases:
providers: [
provideAppInitializer(() => {
const authService = inject(AuthService);
// Direct initialization code here
return authService.initialize();
})
]
Using Optional Migration Command
Migration Note
After updating Angular, run this optional migration to handle replacements automatically:
ng update @angular/core --name provide-initializer
You should see this confirmation during migration:
Optional migrations of package '@angular/core'
Replaces `APP_INITIALIZER`, `ENVIRONMENT_INITIALIZER` &
`PLATFORM_INITIALIZER` respectively with `provideAppInitializer`,
`provideEnvironmentInitializer` & `providePlatformInitializer`.
Key Implementation Details
Complete Working Example
// app.config.ts
import { ApplicationConfig, inject } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideAppInitializer } from '@angular/core';
import { AuthService } from './auth.service';
const initializeAuth = () => {
const authService = inject(AuthService);
return () => authService.initAuthFlow();
};
export const appConfig: ApplicationConfig = {
providers: [
provideRouter([]),
provideAppInitializer(initializeAuth)
]
};
Important Considerations
Dependency Injection: Use
inject()
only within:- Constructor functions
- Factory functions
- Arrow functions provided to
provideAppInitializer
Asynchronous Initialization: Return a
Promise
for async operations:
provideAppInitializer(() => {
const configService = inject(ConfigService);
return configService.loadAppConfig();
})
- Multiple Initializers: The
multi
option is now implicit:
providers: [
provideAppInitializer(initAuth),
provideAppInitializer(loadConfig),
provideAppInitializer(setAnalytics)
]
- Error Handling: Wrap initialization logic in try/catch:
provideAppInitializer(() => {
try {
const service = inject(CriticalService);
return service.init();
} catch (error) {
console.error('Initialization failed', error);
return Promise.resolve(); // Prevent app termination
}
})
Troubleshooting Common Issues
Warning: Incorrect Context
Ensure you never call inject()
in these contexts:
- Top-level module scope
- Outside Angular execution flow
- Class property initializers
- Plain JavaScript functions without wrapping
Solution: Always wrap initialization code in arrow functions. The function passed to provideAppInitializer
automatically inherits the injection context.
Migration Checklist
- Find all
APP_INITIALIZER
providers - Run
ng update @angular/core --name provide-initializer
- Check each replacement wraps
inject()
calls in functions - Test synchronous and async initializers
- Verify dependency tree order remains unchanged
By properly wrapping initializers and using dependency injection within valid contexts, you eliminate the NG0203 error while embracing Angular 19's standardized provider patterns.