In a standalone Angular 22 app, how is HttpClient made available, and when do you still need provideHttpClient()?
answer
- available in root since v21
- features are the reason to configure
- with-functions passed as arguments
- environment providers, not component ones
- the old NgModule is deprecated
basics
~10 sSince Angular 21, HttpClient is provided in root, so inject(HttpClient) works with no setup. provideHttpClient() in the application providers is still how you add features: withInterceptors, withInterceptorsFromDi, withXsrfConfiguration or withNoXsrfProtection, withXhr and withRequestsMadeViaParent.
solid answer
~30 s`HttpClient` has been **provided in root** since Angular 21, so a service can `inject(HttpClient)` without any configuration and gets the defaults: in v22 the `FetchBackend` and the built-in XSRF interceptor. You call `provideHttpClient(...)` in `app.config.ts` (or an `NgModule`'s or a route's `providers`) when you need to **change** that configuration, by passing feature functions: `withInterceptors([...])` for functional interceptors, `withInterceptorsFromDi()` for class-based ones, `withXsrfConfiguration({...})` or `withNoXsrfProtection()`, `withXhr()` for the XMLHttpRequest backend, and `withRequestsMadeViaParent()` for nested injectors. It returns `EnvironmentProviders`, so it belongs in application or route providers, not in a component's `providers`. `HttpClientModule` is deprecated and equals `provideHttpClient(withInterceptorsFromDi(), withXhr())`.
code
ts · 31 lines// app.config.ts
import {ApplicationConfig} from '@angular/core';
import {provideHttpClient, withInterceptors, withXsrfConfiguration} from '@angular/common/http';
import {authInterceptor} from './auth.interceptor';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(
withInterceptors([authInterceptor]),
withXsrfConfiguration({cookieName: 'CSRF-TOKEN', headerName: 'X-CSRF-TOKEN'}),
),
],
};
// files.service.ts: HttpClient is injected the same way with or without provideHttpClient()
import {HttpClient} from '@angular/common/http';
import {inject, Injectable} from '@angular/core';
export interface StoredFile {
id: string;
name: string;
}
@Injectable({providedIn: 'root'})
export class FilesService {
private readonly http = inject(HttpClient);
list() {
return this.http.get<StoredFile[]>('/api/files');
}
}go deeper
Know that HttpClient is injected with inject(HttpClient), that provideHttpClient() goes in app.config.ts, and that features like withInterceptors are passed to it as arguments.
Explain which defaults apply with no configuration in v22, list the feature functions, and map HttpClientModule to its provider equivalent including withXhr().
Decide where each client configuration lives (application versus route injectors) and plan the migration off HttpClientModule without changing the backend by accident.
Set a team convention for HTTP configuration in a large app: one root configuration, when route-level clients are allowed, and how interceptors are owned.
## What changed over the last few majors For years the answer to "how do I set up HttpClient?" was "import `HttpClientModule`". That answer is now two steps out of date: 1. **v15 onward**: standalone applications configure the client with `provideHttpClient()` in `bootstrapApplication`'s providers; `HttpClientModule` was deprecated in v18. 2. **v21**: the HTTP services became **provided in root**, so `inject(HttpClient)` works even if nothing configures it. 3. **v22**: the default backend became **`FetchBackend`**, the browser `fetch` API, and `withFetch()` was deprecated because it is now the default. ## What you get with no configuration With no `provideHttpClient()` call at all, an Angular 22.2 application can inject `HttpClient` and it uses: - `FetchBackend` as the `HttpBackend`; - the built-in XSRF interceptor with its default cookie and header names; - no application interceptors. ## When provideHttpClient() is still needed `provideHttpClient(...features)` exists to **configure** the client. Each feature is a function whose result you pass as an argument: | Feature | What it configures | |---|---| | `withInterceptors([fn, ...])` | functional interceptors (`HttpInterceptorFn`), run in array order | | `withInterceptorsFromDi()` | class-based interceptors registered under the `HTTP_INTERCEPTORS` token | | `withXsrfConfiguration({cookieName, headerName})` | custom XSRF cookie and header names | | `withNoXsrfProtection()` | turns the XSRF interceptor off | | `withXhr()` | switches the backend to `HttpXhrBackend` (needed for upload progress) | | `withRequestsMadeViaParent()` | sends requests on to the parent injector's `HttpClient` | | `withJsonpSupport()` | JSONP requests; deprecated since 22.1 | | `withFetch()` | deprecated no-op since v22: fetch is already the default | Two combinations are rejected in development mode with a configuration error: `withXsrfConfiguration()` together with `withNoXsrfProtection()`, and `withRequestsMadeViaParent()` together with `withFetch()` or `withXhr()`. ## Where the call goes `provideHttpClient()` returns **`EnvironmentProviders`**. That type is accepted by: - `ApplicationConfig.providers` in `app.config.ts` (the usual place); - an `NgModule`'s `providers` in module-based apps; - a route's `providers`, which creates a separate client for that part of the app. It is **not** accepted in a component's `providers`, because components do not have an environment injector. ## The NgModule equivalents Codebases that still import the NgModules can map them directly: | NgModule (deprecated) | Provider-function equivalent | |---|---| | `HttpClientModule` | `provideHttpClient(withInterceptorsFromDi(), withXhr())` | | `HttpClientXsrfModule.withOptions(...)` | `withXsrfConfiguration(...)` | | `HttpClientXsrfModule.disable()` | `withNoXsrfProtection()` | | `HttpClientJsonpModule` | `withJsonpSupport()` | Note the `withXhr()` in the first row: an app that still imports `HttpClientModule` keeps the XMLHttpRequest backend even in v22, while a plain `provideHttpClient()` uses fetch. ## How features compose `provideHttpClient()` is variadic: each feature function returns a small descriptor carrying a kind and a list of providers, and `provideHttpClient()` appends those providers after its own defaults. Three consequences follow: - **features are tree-shakable**: an app that never calls `withXhr()` does not need the XHR backend wired in; - **order matters for interceptors**: the built-in XSRF interceptor is registered first, then each feature's interceptors in the order the features are passed; - **contradictions are detectable**: because every feature declares its kind, Angular can reject impossible combinations at startup in development mode. ## Common setup mistakes 1. Passing an options object such as `{interceptors: [...]}`; the API takes feature-function results as separate arguments. 2. Adding `withFetch()` in new v22 code; it is deprecated and changes nothing. 3. Registering class interceptors under `HTTP_INTERCEPTORS` but forgetting `withInterceptorsFromDi()`, so they never run. 4. Putting `provideHttpClient()` in a lazy route without realising it creates a separate client that ignores the root interceptors. ## A practical default for a new app - Leave `HttpClient` unconfigured until you need a feature. - When you add an interceptor, add `provideHttpClient(withInterceptors([...]))` once, in `app.config.ts`. - Prefer functional interceptors; `withInterceptorsFromDi()` is for existing class-based ones, and the source notes that support for DI-provided interceptors may be phased out later.
- Why can't provideHttpClient() go in a component's providers array?It returns `EnvironmentProviders`, which can only be installed in an environment injector: the application's, an `NgModule`'s or a route's. Component providers live in the element injector tree, so the type is rejected. For a separate client in one area of the app, use a lazy route's `providers`.
- What happens if you pass both withXsrfConfiguration() and withNoXsrfProtection()?In development mode `provideHttpClient()` throws a configuration error, because customising XSRF names and disabling XSRF protection contradict each other. Pick one.
saying these in an interview costs you the question
- HttpClient cannot be injected until HttpClientModule is imported
- provideHttpClient() can be added to a component's providers to scope it
- withFetch() must be added in Angular 22 to use the fetch API
- provideHttpClient() takes a configuration object such as {interceptors: [...]}
- HttpClientModule and a bare provideHttpClient() configure identical clients in v22