npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ngx-openapi-client

v0.3.0

Published

Type-safe, Observable-based HTTP client for Angular, powered by openapi-typescript-generated types

Readme

ngx-openapi-client

Angular Vitest License: MIT CI/CD Pipeline codecov Known Vulnerabilities

A type-safe, Observable-based HTTP client for Angular, powered by your OpenAPI spec.

ngx-openapi-client wraps Angular's HttpClient with types generated by openapi-typescript. Paths, path/query parameters, request bodies, and response bodies are all checked at compile time against your API spec — with zero runtime overhead beyond a thin request wrapper, and full participation in Angular's DI and interceptor pipeline.

const client = createClient<paths>({ baseUrl: 'https://api.example.com' });

client.GET('/pets/{id}', { params: { path: { id: 42 } } });
// ✅ URL, params, and response typed from your OpenAPI spec
// ❌ client.GET('/petz')          — compile error: unknown path
// ❌ path: { id: 'not-a-number' } — compile error: id must be number

Design goals

This library is deliberately small. It exists to make one workflow safe and effortless — and to encourage you to keep it that way:

  • The spec is the single source of truth. API types are generated from your OpenAPI document, never written by hand. Regenerate on every install and when the API changes, the compiler — not a runtime error in production — points at every call site that broke.
  • Zero footprint. The entire type layer erases at compile time. The only code you ship is a thin delegation to Angular's HttpClient — no parallel HTTP stack, no response transformation, no hidden state. What goes over the wire is exactly what HttpClient would send.
  • Angular-native. Requests run through your existing DI, interceptors, and HttpContext; tests keep using HttpTestingController. Nothing to configure twice.
  • Signals over subscriptions. Every method returns an Observable so it composes with the ecosystem — but don't .subscribe() yourself. Feed it to the resource API (rxResource) and consume value() / isLoading() / error() declaratively; Angular manages the subscription lifecycle for you.

Installation

npm install ngx-openapi-client
npm install --save-dev openapi-typescript

Requires Angular ≥ 21.2 and rxjs ≥ 7.4 (peer dependencies).

Support policy: the library supports every Angular major that is still within its official support window (currently 21 and 22). Support for a major is dropped — as a semver-major release of this library — once it leaves Angular LTS.

Generate types from your spec

npx openapi-typescript ./openapi/api.yaml -o ./src/app/api/schema.ts

Tip: wire it to your prepare script so types regenerate on every install.

Usage

createClient resolves HttpClient via inject(), so call it in an injection context — a field initializer of a service is the natural place:

import { Injectable } from '@angular/core';
import type { HttpResponse } from '@angular/common/http';
import type { Observable } from 'rxjs';
import { createClient } from 'ngx-openapi-client';
import type { SchemaOf } from 'ngx-openapi-client';
import type { components, paths } from './api/schema';

type Pet = SchemaOf<components, 'Pet'>;

@Injectable({ providedIn: 'root' })
export class PetsApi {
  private readonly client = createClient<paths>({
    baseUrl: 'https://api.example.com',
    headers: { 'X-Api-Version': '2' }, // sent with every request
  });

  listPets(search?: string): Observable<HttpResponse<Pet[]>> {
    return this.client.GET('/pets', { params: { query: { search } } });
  }

  createPet(name: string): Observable<HttpResponse<Pet>> {
    return this.client.POST('/pets', { body: { name } });
  }

  deletePet(id: number): Observable<HttpResponse<never>> { // 204 — no body
    return this.client.DELETE('/pets/{id}', { params: { path: { id } } });
  }
}

All seven methods are available: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS. Each accepts only the paths that declare that method in your spec.

Responses and errors

Every method returns a cold Observable of Angular's raw HttpResponse<SuccessBody> — status, headers, and the typed body are all there, and nothing is hidden behind a wrapper. Consume it as a signal with Angular's resource API instead of subscribing manually:

import { Component, inject, signal } from '@angular/core';
import { rxResource } from '@angular/core/rxjs-interop';

@Component({ /* ... */ })
export class PetListComponent {
  private readonly petsApi = inject(PetsApi);

  readonly search = signal('');

  readonly pets = rxResource({
    params: () => ({ search: this.search() }),
    stream: ({ params }) => this.petsApi.listPets(params.search),
  });
}
@if (pets.value(); as response) {
  <ul>
    @for (pet of response.body; track pet.id) {
      <li>{{ pet.name }}</li>
    }
  </ul>
} @else if (pets.isLoading()) {
  <p>Loading…</p>
}

Updating the search signal re-triggers the request automatically; pets.value() is the typed HttpResponse<Pet[]>.

Non-2xx responses follow Angular convention and arrive on the error channel as HttpErrorResponse — with rxResource that surfaces as pets.error(). Interceptors, retry, catchError, and everything else in your existing HTTP setup work unchanged.

Typed error bodies

HttpErrorResponse.error is any, so by itself the error body loses all contract information. createErrorGuard restores it without a single cast: build a guard for one operation from its ErrorResponses map, and both the accepted status codes and the narrowed body types come straight from your spec.

import { computed } from '@angular/core';
import { createErrorGuard } from 'ngx-openapi-client';
import type { ErrorResponses } from 'ngx-openapi-client';

const isListPetsError = createErrorGuard<ErrorResponses<paths, '/pets', 'get'>>();

// inside PetListComponent
readonly errorMessage = computed(() => {
  const err = this.pets.error();
  if (isListPetsError(err, 400)) {
    return err.error.message; // ApiError — fully typed, no cast
  }
  return err ? 'Something went wrong.' : undefined;
});

The code argument autocompletes to exactly the response keys your spec declares for that operation: numeric status codes, range keys like '5XX', and 'default'. Two things to know:

  • Declared codes are erased at runtime, so a range or 'default' check cannot exclude codes the spec also declares explicitly. Order your branches specific → range → 'default' — the same precedence OpenAPI itself defines.
  • 'default' never matches network-level failures (status 0, e.g. timeout or offline); those carry a ProgressEvent instead of a contract body.

Per-request options

this.client.GET('/pets/{id}', {
  params: { path: { id }, query: { verbose: true } },
  headers: { 'X-Request-Id': requestId }, // merged over client headers; request wins
  context: new HttpContext(),             // passed to Angular interceptors
});

Security

  • Path parameters are percent-encoded per segment; . is additionally encoded as %2E, so a user-supplied value like ../admin cannot be normalised into a path traversal by the server.
  • Query parameters: &, #, and % in values stay percent-encoded — a value cannot inject additional parameters or truncate the query string.
  • Headers are always passed to HttpClient as a structured record, never as a raw string — a CRLF in a header value cannot inject an additional header.

Each of these invariants is pinned by a regression test.

Scope and limitations

  • Path parameters: OpenAPI style simple (the default) with scalar values. label/matrix styles and array/object values are not supported.
  • Query parameters: scalar values. Arrays, spaceDelimited, pipeDelimited, and deepObject styles are not supported. Note that Angular's default codec sends a literal + in values (servers decode it as a space).
  • Cookie parameters (in: cookie) are out of scope.
  • Request/response bodies: application/json only.

Exported types

Besides createClient and createErrorGuard, the package exports the type utilities it is built from, usable in your own signatures: ClientOptions, RequestInit, NgOpenApiClient, HttpMethod, PathsWithMethod, RequestParams, RequestBody, SuccessResponseBody, ErrorResponseBody, ErrorResponses, TypedHttpErrorResponse, SchemaOf, SchemaName.

License

MIT