Skip to content

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:

typescript
// Angular 18 approach
providers: [
  { 
    provide: APP_INITIALIZER, 
    useFactory: initializeApp1, 
    deps: [AuthService], 
    multi: true 
  },
]
typescript
// 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:

typescript
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:

typescript
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:

bash
ng update @angular/core --name provide-initializer

You should see this confirmation during migration:

text
Optional migrations of package '@angular/core'
  
Replaces `APP_INITIALIZER`, `ENVIRONMENT_INITIALIZER` & 
`PLATFORM_INITIALIZER` respectively with `provideAppInitializer`, 
`provideEnvironmentInitializer` & `providePlatformInitializer`.

Key Implementation Details

Complete Working Example

typescript
// 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

  1. Dependency Injection: Use inject() only within:

    • Constructor functions
    • Factory functions
    • Arrow functions provided to provideAppInitializer
  2. Asynchronous Initialization: Return a Promise for async operations:

typescript
provideAppInitializer(() => {
  const configService = inject(ConfigService);
  return configService.loadAppConfig();
})
  1. Multiple Initializers: The multi option is now implicit:
typescript
providers: [
  provideAppInitializer(initAuth),
  provideAppInitializer(loadConfig),
  provideAppInitializer(setAnalytics)
]
  1. Error Handling: Wrap initialization logic in try/catch:
typescript
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

  1. Find all APP_INITIALIZER providers
  2. Run ng update @angular/core --name provide-initializer
  3. Check each replacement wraps inject() calls in functions
  4. Test synchronous and async initializers
  5. 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.