Skip to main content

Introduction

HttpClient gives you a request. It doesn't give you a policy — where the base URL comes from, how a failed response becomes something your components can render, which requests are safe to retry, or how a loading indicator knows anything is in flight.

Most Angular apps end up re-solving all four in an ad-hoc ApiService that grows one special case at a time. ngx-api-client is that service, extracted and made configurable.

// This is the whole of a typical data service.
@Injectable({ providedIn: 'root' })
export class OrderService {
private readonly api = inject(ApiService);

list(page: number, size: number): Observable<PaginatedResponse<Order>> {
return this.api.getPage<Order>('/orders', page, size);
}
}

GET /orders resolves to https://api.example.com/api/v1/orders?page=0&size=20, retries on a 503, normalises a failure into one ApiError shape, and toggles a loading signal on the way — all from configuration you wrote once.

The path of one request​

Everything above the handlers is the library's job. The two handlers at the bottom are the seam where your application decides what a failure or a success looks like.

What it does​

ConcernWhat the library provides
URL buildingbaseUrl + optional prefix segment + optional version segment + endpoint
VersioningURL segment, query parameter, header or media type — or off; overridable per request
ErrorsEvery failure normalised into one ApiError shape (RFC 9457)
RetryExponential backoff with jitter, Retry-After support, skips non-idempotent methods
Per-request optsCarried to interceptors through HttpContext, not globals
PresentationPluggable — the library renders nothing; you decide what an error or a success message looks like
Loading stateA signal that counts in-flight requests

What it deliberately does not do​

  • It renders nothing. No toasts, no dialogs, no redirects. provideApi() registers presentation-free fallbacks and expects you to supply an adapter for your own UI kit. See Error handling.
  • It brings no UI dependency. The peer dependencies are @angular/core, @angular/common and rxjs. That is the entire list.
  • It carries no i18n dependency. Success messages are plain strings you pass in already translated.
  • It does not register interceptors for you. Order matters, and only you know what else is in your chain — so you wire them up in withInterceptors() yourself.

Design principles​

Configuration over convention, defaults over configuration. Every option has a default that suits a conventional REST API. Every default can be replaced, and every global can be overridden for a single request.

Per-request options travel with the request. Options are attached to the request's HttpContext, so an interceptor reads what this call asked for rather than consulting mutable shared state — which keeps concurrent requests from interfering with each other.

One error shape. A problem+json body, a plain-text proxy error page and a network drop that never reached the server all arrive at your code as the same ApiError. Components branch on code, never on prose.

Compatibility​

Supports Angular 17 through 22. Every one of those majors is verified on each push against the packed tarball — type-checked, built into a real consumer application, and run — on both Node 22 and 24. The upper bound is deliberate: a new major is added only once it has been validated.

See Installation for the full requirements.

Next steps​