| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Requires Angular 22+ and Nx 22+. The generated code uses httpResource(), which is only available from Angular 22 onwards.
An Angular 22 · Nx monorepo that demonstrates tree-shakeable, signal-native API clients generated from OpenAPI 3.x specs.
The core idea: one InjectionToken per API endpoint, each in its own .ts file. Because esbuild tree-shakes at file boundaries, any token you never inject() costs zero bytes in your bundle.
| Path | Type | Description |
|---|---|---|
| tools/openapi-resource-gen/ | Nx generator · npm package | Reads an OpenAPI spec, emits one token file per endpoint |
| tools/openapi-resource-mocks/ | npm package | Zero-HTTP mock bus for generated tokens — Playwright E2E + Chrome Extension integration |
| tools/openapi-resource-devtools/ | Chrome Extension shell | Manifest, content script, service worker, devtools page |
| apps/devtools-panel/ | Angular 22 app | Panel UI bundled inside the Chrome Extension |
| libs/github-data-access/ | Generated data-access lib | GitHub REST API (~38 endpoints used) |
| libs/petstore-data-access/ | Generated data-access lib | OAI Petstore v3 (12 endpoints) |
| libs/weather-data-access/ | Generated data-access lib | Open-Meteo forecast API |
| libs/youtube-data-access/ | Generated data-access lib | YouTube Data API v3 (76 endpoints) |
| apps/api-explorer/ | Angular 22 app | Demo app that consumes all data-access libs |
Published on npm: @constantant/openapi-resource-gen
Step 1 — install the generator (once per workspace):
npm install -D @constantant/openapi-resource-genStep 2 — generate a data-access lib from any OpenAPI 3.x spec (local file or URL):
npx nx g @constantant/openapi-resource-gen:api-resource \
--specPath=https://petstore3.swagger.io/api/v3/openapi.yaml \
--outputDir=libs/petstore-data-access/src \
--baseUrlToken=PETSTORE_BASE_URLStep 3 — wire up providers and inject in your component:
// app.config.ts
import { provideHttpClient } from '@angular/common/http';
import { PETSTORE_BASE_URL, provideFindPetsByStatus } from './libs/petstore-data-access/src';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(),
{ provide: PETSTORE_BASE_URL, useValue: 'https://petstore3.swagger.io/api/v3' },
provideFindPetsByStatus(),
],
};// pets-page.component.ts
@Component({ ... })
export class PetsPageComponent {
private findPetsByStatus = inject(FIND_PETS_BY_STATUS);
readonly status = signal<'available' | 'pending' | 'sold'>('available');
readonly pets = this.findPetsByStatus(() => ({ status: this.status() }));
}Re-run the generator command whenever your spec changes — it overwrites generated files and removes any that no longer exist in the spec.
| Option | Required | Default | Description |
|---|---|---|---|
| specPath | yes | — | Local path or https:// URL to the OpenAPI 3.x YAML or JSON spec |
| outputDir | yes | — | Output directory relative to workspace root |
| baseUrlToken | no | API_BASE_URL | Name of the base-URL injection token |
| tagFilter | no | all tags | Comma-separated list of tags to include |
| namingConvention | no | kebab | kebab or camel — controls file names |
| providedIn | no | none | none (use provideX() helpers) or root (self-registering) |
| includeMocks | no | false | Co-generate .mock.ts providers, index.mock.ts barrels, and mocks.manifest.json — requires @constantant/openapi-resource-mocks |
| includeMswHandlers | no | false | Co-generate .msw.ts MSW 2.x handler files and index.msw.ts barrels — requires msw >= 2.0.0 |
| specId | no | derived | Identifier embedded in MockResourceMeta and mocks.manifest.json. Defaults to baseUrlToken with _BASE_URL stripped (e.g. PETSTORE_BASE_URL → petstore). Must match when importing into the DevTools panel. |
| verbose | no | false | Print a +/~/- summary of created, updated, and deleted files after generation. |
See tools/openapi-resource-gen/README.md for full documentation, or the step-by-step tutorials.
Every endpoint becomes a typed InjectionToken whose factory returns an httpResource. For GET endpoints with query params, the reactive lambda uses a block-body form so it can return undefined to suppress the request when a thunk-based params arg returns undefined:
// libs/petstore-data-access/src/pet/find-pets-by-status.token.ts
import { InjectionToken, inject, FactoryProvider } from '@angular/core';
import { httpResource } from '@angular/common/http';
import type { paths } from '../schema.d';
import { PETSTORE_BASE_URL } from '../api-base-url.token';
export type FindPetsByStatusParams =
paths['/pet/findByStatus']['get']['parameters']['query'];
export type FindPetsByStatusResponse =
paths['/pet/findByStatus']['get']['responses']['200']['content']['application/json'];
export const FIND_PETS_BY_STATUS = new InjectionToken<
(params?: FindPetsByStatusParams | (() => FindPetsByStatusParams | undefined))
=> ReturnType<typeof httpResource<FindPetsByStatusResponse>>
>('FIND_PETS_BY_STATUS');
export function provideFindPetsByStatus(): FactoryProvider {
return {
provide: FIND_PETS_BY_STATUS,
useFactory: () => {
const base = inject(PETSTORE_BASE_URL);
return (params?) =>
httpResource<FindPetsByStatusResponse>(() => {
const _params = typeof params === 'function' ? params() : params;
if (typeof params === 'function' && _params === undefined) return undefined;
return {
url: `${base}/pet/findByStatus`,
params: _params as unknown as Record<string, string | number | boolean | readonly (string | number | boolean)[]>,
};
});
},
};
}Key properties of every generated file:
import { provideHttpClient } from '@angular/common/http';
import { PETSTORE_BASE_URL, provideFindPetsByStatus } from '@angular-openapi-gen/petstore-data-access';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(),
{ provide: PETSTORE_BASE_URL, useValue: 'https://petstore3.swagger.io/api/v3' },
provideFindPetsByStatus(),
],
};@Component({ ... })
export class PetsPageComponent {
private findPetsByStatus = inject(FIND_PETS_BY_STATUS);
readonly status = signal<'available' | 'pending' | 'sold'>('available');
// Thunk → httpResource re-fetches whenever status() changes
readonly pets = this.findPetsByStatus(() => ({ status: this.status() }));
}@if (pets.isLoading()) { <mat-progress-bar mode="indeterminate" /> }
@for (pet of pets.value() ?? []; track pet.id) {
<p>{{ pet.name }}</p>
}Pass a thunk that returns undefined to suppress the request until conditions are met:
// No request fires until both apiKey and query are set
readonly results = this.youtubeSearch(() =>
this.apiKey() && this.query()
? { q: this.query(), key: this.apiKey()! }
: undefined
);# Serve the demo app
npx nx serve api-explorer
# Production build
npx nx build api-explorer
npx nx build api-explorer --stats-json # include esbuild bundle stats
# Run tests
npx nx test openapi-resource-gen # generator unit tests
npx nx e2e api-explorer-e2e # Playwright E2E tests
# Lint everything
npx nx run-many -t lint
# Type-check everything
npx nx run-many -t typecheck
# Generate from a local file
npx nx g @constantant/openapi-resource-gen:api-resource \
--specPath=specs/myapi.yaml \
--outputDir=libs/myapi-data-access/src \
--baseUrlToken=MYAPI_BASE_URL
# Generate from a URL (no curl step needed)
npx nx g @constantant/openapi-resource-gen:api-resource \
--specPath=https://petstore3.swagger.io/api/v3/openapi.yaml \
--outputDir=libs/petstore-data-access/src \
--baseUrlToken=PETSTORE_BASE_URL
# Or declare a generate target in project.json and run:
npx nx run petstore-data-access:generatePublished on npm: @constantant/openapi-resource-mocks
A companion package that provides zero-HTTP, pure-DI mocks for generated tokens. Key features:
See tools/openapi-resource-mocks/README.md for full documentation.
Current version: 0.7.0 | Status: pending Chrome Web Store review
A Chrome DevTools panel that connects to any Angular app running @constantant/openapi-resource-mocks. It lists every registered mock token, shows live state, and lets you resolve, fail, catch, or reset mocks without touching code.
Key panel features:
Chrome Web Store: pending review — use Load unpacked for now.
See tools/openapi-resource-devtools/README.md for full documentation.
The generator is released via the Release GitHub Actions workflow (.github/workflows/release.yml), triggered manually from the Actions tab.
What the workflow does:
The workflow is idempotent — if the current version is already on npm it skips publishing gracefully.
Note on nx release commit detection: nx release counts only commits that touch files within tools/openapi-resource-gen/. Workflow-only changes (e.g. editing .github/) do not trigger a version bump.
Note on branch protection: master is protected (PRs require CI + a code-owner review, linear history). The release workflow checks out with GH_PAT (a repo admin PAT stored in GitHub secrets) instead of GITHUB_TOKEN — GITHUB_TOKEN cannot bypass branch protection's required status checks even with enforce_admins: off, so the version-bump commit and tag push would fail without a PAT.
The Chrome Extension is released via a separate Release Extension workflow (.github/workflows/release-extension.yml). It bumps manifest.json, builds and zips the extension, creates a GitHub Release, and uploads to the Chrome Web Store. Required secrets: GH_PAT, CHROME_EXTENSION_ID, CHROME_PUBLISHER_ID, CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN.
Contributions are welcome! Please read CONTRIBUTING.md before opening a PR — in particular the rule that generated code under libs/*/src/ is never hand-edited (fix the generator and regenerate instead).
| Back | FazBrowse Home | New Git URL |