Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

ngx-openapi-typescript

Type-safe Angular resource signals and HTTP client backed by openapi-typescript.

This library provides two main utilities:

  • createOpenApiResource — a reactive factory built on Angular's httpResource that gives you compile-time type safety for every HTTP request and response shape defined in your OpenAPI schema.
  • OpenApiHttpClient — a typed wrapper around Angular's HttpClient for imperative (non-reactive) HTTP calls.

Both are driven by the types generated by openapi-typescript, so typos in paths, wrong parameter names, or incorrect body shapes are caught at compile time, not at runtime.

Installation

npm install ngx-openapi-typescript openapi-typescript-helpers

This package is built for Angular ^21.2.0 and ^22.0.0 and expects these peer dependencies in your app:

  • @angular/common ^21.2.0 || ^22.0.0
  • @angular/core ^21.2.0 || ^22.0.0
  • openapi-typescript-helpers ^0.1.0

If you want to generate types locally, also install the generator as a dev dependency:

npm install -D openapi-typescript

Generating types

Generate type definitions by pointing openapi-typescript at your OpenAPI spec:

npx openapi-typescript https://api.example.com/openapi.yaml -o src/api.types.ts

This creates a paths interface that both utilities consume.

API

createOpenApiResource

A factory for creating reactive Angular resource signals. Must be called inside an Angular injection context (component/service field initializer or constructor).

import type { paths } from './api.types';
import { createOpenApiResource } from 'ngx-openapi-typescript';

@Component({ ... })
export class PetListComponent {
  private readonly api = createOpenApiResource<paths>({
    baseUrl: 'https://api.example.com',
  });

  // Simple GET — no params required
  protected readonly pets = this.api('/pets');

  // Reactive GET with params — re-fetches when the signals read inside the lambda change
  protected readonly petsLimited = this.api('/pets', () => ({
    params: { query: { limit: 10 } },
  }));

  // Pass signal-driven path params — re-fetches when petId() changes
  private readonly petId = signal(1);
  protected readonly pet = this.api('/pets/{petId}', () => ({
    params: { path: { petId: this.petId() } },
  }));

  // Return undefined to put the resource in Idle state (no request made)
  private readonly enabled = signal(false);
  protected readonly lazyPets = this.api('/pets', () =>
    this.enabled() ? {} : undefined,
  );
}

Method Override

The factory defaults to GET. You can pass a method option to use a different HTTP method, but Angular recommends using httpResource only for reads. Prefer OpenApiHttpClient for mutations.

// Explicit method override (not recommended for production use)
protected readonly createResult = this.api('/pets', () => ({
  body: { name: 'Buddy' },
}), { method: 'post' });

OpenApiHttpClient

A base class that wraps Angular's HttpClient with OpenAPI types. Ideal for imperative calls, mutations, or service-layer code.

import type { paths } from './api.types';
import { OpenApiHttpClient } from 'ngx-openapi-typescript';
import { Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class PetApiClient extends OpenApiHttpClient<paths> {
  constructor() {
    super({ baseUrl: 'https://api.example.com' });
  }
}
// In a component/service
@Component({ ... })
export class PetDetailComponent {
  private readonly api = inject(PetApiClient);

  load(id: number) {
    this.api
      .get('/pets/{petId}', { params: { path: { petId: id } } })
      .subscribe(pet => console.log(pet)); // pet is typed as Pet
  }

  create(name: string) {
    this.api
      .post('/pets', { body: { name } })
      .subscribe(newPet => console.log(newPet));
  }
}

Custom HTTP client

By default, OpenApiHttpClient injects Angular's configured HttpClient and uses its interceptors. If you need a second API client with a differently configured HttpClient, pass that instance through the httpClient option. Both clients retain the same generated endpoint, parameter, body, and response types.

For example, inside an Angular injection context:

import { HttpBackend, HttpClient } from '@angular/common/http';
import { inject } from '@angular/core';
import { OpenApiHttpClient } from 'ngx-openapi-typescript';
import type { paths } from './api.types';

// Uses Angular's default HttpClient; requires an injection context.
const api = new OpenApiHttpClient<paths>({
  baseUrl: 'https://api.example.com',
});

// Uses a separate HttpClient instance.
const secondApi = new OpenApiHttpClient<paths>({
  baseUrl: 'https://second-api.example.com',
  httpClient: new HttpClient(inject(HttpBackend)),
});

When you supply an existing HttpClient instance, OpenApiHttpClient can also be constructed outside an Angular injection context. The example above requires one because it calls inject(HttpBackend).

createOpenApiResource cannot accept a custom HttpClient, because Angular's underlying httpResource does not offer this option. See Angular issue #62616.

Response formats and full responses

OpenApiHttpClient follows Angular's responseType and observe options on get, post, put, patch, and delete.

responseType Response body type
omitted or 'json' Generated OpenAPI success-response type
'blob' Blob
'arraybuffer' ArrayBuffer
'text' string
observe Return type
omitted or 'body' Observable<Body>
'response' Observable<HttpResponse<Body>>
'events' Observable<HttpEvent<Body>>

For example, an endpoint whose schema declares application/pdf with type: string and format: binary can be read as a blob:

api.get('/documents/{id}', {
  params: { path: { id: 123 } },
  responseType: 'blob',
}); // Observable<Blob>

api
  .get('/documents/{id}', {
    params: { path: { id: 123 } },
    responseType: 'blob',
    observe: 'response',
  })
  .subscribe((response) => {
    const disposition = response.headers.get('Content-Disposition');
    const pdf = response.body; // Blob | null, as in Angular's HttpResponse
  });

Use events and reportProgress when you need download progress:

import { HttpEventType } from '@angular/common/http';

api
  .get('/documents/{id}', {
    params: { path: { id: 123 } },
    responseType: 'blob',
    observe: 'events',
    reportProgress: true,
  })
  .subscribe((event) => {
    if (event.type === HttpEventType.DownloadProgress) {
      console.log(event.loaded, event.total); // total can be undefined
    } else if (event.type === HttpEventType.Response) {
      console.log(event.body); // Blob | null
    }
  });

Raw response formats are available on any valid endpoint, including JSON endpoints. The selected decoder determines the body type, while paths, parameters, and request bodies remain checked against the generated schema.

Raw response resources

The factory returned by createOpenApiResource mirrors Angular's resource sub-functions.

const resource = createOpenApiResource<paths>({ baseUrl: '/api' });

const pdf = resource.blob('/documents/{id}', () => ({
  params: { path: { id: documentId() } },
  reportProgress: true,
})); // HttpResourceRef<Blob | undefined>

const text = resource.text('/export');
// HttpResourceRef<string | undefined>

const bytes = resource.arrayBuffer('/export');
// HttpResourceRef<ArrayBuffer | undefined>

Each variant supports the same optional request callback, method override, and idle state (undefined from the callback) as the JSON factory. Resources expose headers(), statusCode(), and progress() directly, so they do not take an observe option.

parse receives the raw decoded type and can synchronously transform it. A defined defaultValue removes the implicit undefined from the value type:

const length = resource.text('/export', {
  parse: (text) => text.length,
  defaultValue: 0,
}); // HttpResourceRef<number>

const size = resource.blob(
  '/documents/{id}',
  () => ({
    params: { path: { id: documentId() } },
  }),
  {
    parse: (blob) => blob.size,
  },
); // HttpResourceRef<number | undefined>

Development

# Build the library
ng build ngx-openapi-typescript

# Run tests
ng test ngx-openapi-typescript

# Check compile-time API assertions explicitly
npx tsc -p projects/ngx-openapi-typescript/tsconfig.spec.json --noEmit

# Start the demo app
ng serve demo

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages