angular.courses

From HttpClientModule to provideHttpClient

Register the HTTP client with provideHttpClient() in standalone apps instead of importing HttpClientModule

CLI Migration Script

$ ng generate @angular/core:standalone-bootstrap

Overview

Angular's HTTP client was traditionally registered by importing HttpClientModule in an NgModule. In v14 you could use importProvidersFrom(HttpClientModule) in providers for standalone/hybrid apps. In v15, provideHttpClient() is the preferred API in bootstrap providers. HttpClientModule is deprecated as of v18—migrate to provideHttpClient(). This practice covers the migration and optional features like withFetch(), withInterceptors(), and withInterceptorsFromDi().

Migration guide

v14

Unless you are an early adopter of standalone, wait for the standalone migration script introduced in v15.2 before changing your HTTP setup.

v15

Use the standalone migration in 15.2 to bootstrap your application; it will move HttpClientModule into importProvidersFrom. Because provideHttpClient() is only usable with a standalone application, run the migration first, then remove HttpClientModule from importProvidersFrom and switch to provideHttpClient(). All options available with HttpClientModule are available with provideHttpClient(), so you won't introduce regressions.

withRequestsMadeViaParent() is introduced as a developer preview in v15.1—it's a new feature, so you can start adopting it even in preview if you need it; it's not available with HttpClientModule.

Functional interceptors are introduced in v15 but do not benefit from a migration script. To delay their manual migration, use withInterceptorsFromDi() with provideHttpClient() to support your existing class-based interceptors. Once the interceptor migration is done, you can switch to withInterceptors().

v16

No changes to HTTP client setup.

v16.1

XMLHttpRequest is not supported in all environments. You can use the withFetch() feature, even if still in developer preview.

v17

No changes to HTTP client setup.

v17.1

withFetch() is now stable. You can add it if you delayed the change due to the preview status of the feature.

v18

HttpClientModule is deprecated; migrate to provideHttpClient().

v19

withRequestsMadeViaParent() is now stable. You can add it if you delayed the change due to the preview status of the feature.

v20

No changes to HTTP client setup.

v21

provideHttpClient() is now provided by default. It won't affect existing applications. You'll need to add it explicitly if you need to pass options (such as withFetch()).

The Evolution

v2HttpClientModule
Import HttpClientModule in NgModule to register HttpClient and XSRF/JSON support.
v14importProvidersFrom(HttpClientModule)
Use importProvidersFrom(HttpClientModule) in providers for standalone/ hybrid setup.
v15provideHttpClient()
Standalone API: provideHttpClient() in bootstrapApplication providers.
v15.1withRequestsMadeViaParent() (developer preview)
withRequestsMadeViaParent() available in developer preview to make requests via the parent injector's HttpClient (multi-injector setups).
v16.1withFetch() (developer preview)
withFetch() available in developer preview to use the Fetch API instead of XHR.
v17.1withFetch() stable
withFetch() promoted to stable; use provideHttpClient(withFetch()) for Fetch API backend.
v18HttpClientModule deprecated
HttpClientModule is deprecated; migrate to provideHttpClient().
v19withRequestsMadeViaParent() stable
withRequestsMadeViaParent() promoted to stable; use provideHttpClient(withRequestsMadeViaParent()) so child injectors use the parent's HttpClient.
v21provideHttpClient() provided by default
New app template includes provideHttpClient() by default when using standalone bootstrap.

Code Comparison

Registering the HTTP client

HttpClientModule in NgModuleBefore v2
app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
HttpClientModule
],
bootstrap: [AppComponent]
})
export class AppModule {}
provideHttpClient()v15+
main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { provideHttpClient } from '@angular/common/http';
import { AppComponent } from './app.component';
bootstrapApplication(AppComponent, {
providers: [provideHttpClient()]
});

v14: importProvidersFrom(HttpClientModule)

In Angular v14 you can use importProvidersFrom(HttpClientModule) in your app config or route providers for a standalone/hybrid setup, instead of importing HttpClientModule in an NgModule. In v15, provideHttpClient() is the preferred API.

HttpClientModule deprecated in v18

HttpClientModule is deprecated as of Angular v18. Use provideHttpClient() in your application providers instead. Run the standalone-bootstrap migration to switch from NgModule bootstrap to standalone bootstrap and update HTTP setup.

With functional interceptors

HTTP_INTERCEPTORS in NgModuleBefore v14
@NgModule({
imports: [HttpClientModule],
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
]
})
export class AppModule {}
provideHttpClient(withInterceptors())v15+
import { provideHttpClient, withInterceptors } from '@angular/common/http';
bootstrapApplication(AppComponent, {
providers: [
provideHttpClient(
withInterceptors([authInterceptor])
)
]
});

With DI-based (class) interceptors: withInterceptorsFromDi

To keep using class-based interceptors registered via HTTP_INTERCEPTORS with provideHttpClient(), add withInterceptorsFromDi(). Angular will then pick up any multi provider for HTTP_INTERCEPTORS from your app’s injector.

HTTP_INTERCEPTORS in NgModuleBefore v14
@NgModule({
imports: [HttpClientModule],
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: LoggingInterceptor, multi: true }
]
})
export class AppModule {}
provideHttpClient(withInterceptorsFromDi())v15+
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { AuthInterceptor } from './auth.interceptor';
import { LoggingInterceptor } from './logging.interceptor';
bootstrapApplication(AppComponent, {
providers: [
provideHttpClient(withInterceptorsFromDi()),
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: LoggingInterceptor, multi: true }
]
});

You can combine withInterceptorsFromDi() with withInterceptors() and other features:

provideHttpClient(
withInterceptorsFromDi(),
withInterceptors([authInterceptor]),
withFetch()
)

Fetch backend (optional)

HttpClientModule (XHR)Before v14
// Default: XMLHttpRequest-based
imports: [HttpClientModule]
withFetch()v15+
import { provideHttpClient, withFetch } from '@angular/common/http';
bootstrapApplication(AppComponent, {
providers: [
provideHttpClient(withFetch())
]
});

withFetch() in v16.1

withFetch() was available in developer preview in Angular v16.1. Use it with provideHttpClient() to switch from XMLHttpRequest to the Fetch API (e.g. for SSR or smaller bundle).

Why withFetch() over XMLHttpRequest?

The Fetch API is a more modern API than XMLHttpRequest and is available in environments where XMLHttpRequest is not supported (for example, some server-side or edge runtimes). Using withFetch() can simplify server-side rendering and reduce bundle size when XHR is not needed. withFetch() does have a few limitations: it does not produce upload progress events, so if your app relies on upload progress (e.g. HttpClient request with reportProgress: true for uploads), the default XHR-based backend may be preferable.

Using HttpClient (unchanged)

inject(HttpClient)Before v14
@Injectable({ providedIn: 'root' })
export class UserService {
private http = inject(HttpClient);
getUsers() {
return this.http.get<User[]>('/api/users');
}
}
Same with provideHttpClientv15+
@Injectable({ providedIn: 'root' })
export class UserService {
private http = inject(HttpClient);
getUsers() {
return this.http.get<User[]>('/api/users');
}
}

Key Differences

Registration:HttpClientModule in imports (v2); importProvidersFrom(HttpClientModule) in v14provideHttpClient() in providers (v15)
Interceptors:HTTP_INTERCEPTORS token, multi: truewithInterceptors([fn, ...]) or withInterceptorsFromDi() + HTTP_INTERCEPTORS
Fetch:XHR only (default)withFetch() for fetch API
Usage:inject(HttpClient)Same

Benefits

Standalone

No NgModule; fits bootstrapApplication

Composable

withInterceptors(), withFetch(), withXsrfConfiguration()

Tree-shaking

Provider function is analyzable

Same API

HttpClient usage unchanged

Functional interceptors

withInterceptors() for functional interceptors

provideHttpClient() options

NgModule compatibility

| NgModule | provideHttpClient() equivalent | | --------------------------------------- | --------------------------------------------- | | HttpClientModule | provideHttpClient(withInterceptorsFromDi()) | | HttpClientJsonpModule | withJsonpSupport() | | HttpClientXsrfModule.withOptions(...) | withXsrfConfiguration(...) | | HttpClientXsrfModule.disable() | withNoXsrfProtection() |

  • withInterceptors([...]): Register functional interceptors (see related practice).
  • withInterceptorsFromDi(): Enable DI-based interceptors; any provider for HTTP_INTERCEPTORS (multi: true) will be used. Use this to migrate class-based interceptors without rewriting them as functions.
  • withFetch(): Use the Fetch API instead of XMLHttpRequest (e.g. for SSR or smaller bundle). Available in developer preview in v16.1.
  • withXsrfConfiguration({ ... }): Configure XSRF cookie/header names.
  • withNoXsrf(): Disable XSRF protection.
  • withRequestsMadeViaParent(): Use the parent injector’s HttpClient for requests (useful in multi-injector setups so child injectors share the same client and interceptors).

Multi-injector configurations

When HttpClientModule is present in multiple injectors (e.g. root and a lazy-loaded module), the behavior of interceptors is poorly defined and depends on the exact options and provider/import ordering. Prefer provideHttpClient() for multi-injector configurations, as it has more stable behavior. For child injectors that should use the same HTTP client and interceptors as the parent, use withRequestsMadeViaParent() (see above).

withFetch() and SSR

Using withFetch() can simplify server-side rendering and reduce payload, since the Fetch API is available in Node and browsers.