# ng-openapi — full documentation > Concatenated documentation of ng-openapi (https://ng-openapi.dev), an Angular-first OpenAPI client generator created and maintained by Tareq Jami (https://tareqjami.de) of Jami IT (https://jami-it.de), generated from the same sources as the website. See https://ng-openapi.dev/llms.txt for the index and usage rules. --- ## Introduction ng-openapi is a modern Angular-first OpenAPI client generator that creates type-safe services and interfaces from your OpenAPI specifications. Unlike generic TypeScript generators, ng-openapi is built specifically for Angular developers who want clean, maintainable code that leverages Angular's latest features. ## Quick Example ```bash # Install ng-openapi npm install ng-openapi --save-dev # Generate from OpenAPI spec ng-openapi -i swagger.json -o ./src/api ``` ```typescript // Use in your Angular app import { provideDefaultClient } from './api/providers'; export const appConfig: ApplicationConfig = { providers: [ provideDefaultClient({ basePath: 'https://api.example.com' }) ] }; ``` ## What's Included - **TypeScript Interfaces** - Accurate type definitions from your OpenAPI schemas - **Angular Services** - Injectable services with proper dependency injection - **HTTP Interceptors** - Automatic date transformation and custom headers - **Provider Functions** - Easy setup with `provideDefaultClient()` - **File Utilities** - Download helpers and file handling - **CLI Tool** - Powerful command-line interface with config file support See [Generated Output](./guide/generated-code.md) for a tour of every generated file. ## Support the Project ng-openapi’s mission is to remain the #1 Angular client generation library. If you’d like to support this journey, feel free to sponsor me with a coffee — after all, we all know a developer’s fuel is coffee 😄
Sponsor on GitHub
--- # Installation Install ng-openapi to generate Angular services and TypeScript types from OpenAPI specifications. ## Prerequisites - **Node.js**: Version 20.0.0 or higher - **Angular**: Version 15 or higher (peer dependency) ## Install ### Development Dependency (Recommended) ::: code-group ```bash [npm] npm install ng-openapi --save-dev ``` ```bash [yarn] yarn add ng-openapi --dev ``` ```bash [pnpm] pnpm add ng-openapi --save-dev ``` ::: ### Global Installation ```bash npm install -g ng-openapi ``` ## Verify Installation ```bash ng-openapi --version ``` ## Next Step Continue with the [Quick Start](./quick-start.md) to generate your first client. --- # Quick Start Generate Angular services and TypeScript types from your OpenAPI specification. ## Step 1: Prepare Your OpenAPI Specification You need an OpenAPI/Swagger specification file: - JSON file (`swagger.json`, `openapi.json`) - Yaml file (`swagger.yml`, `openapi.yaml`) ## Step 2: Generate API Client ### Using Command Line ```bash ng-openapi -i ./swagger.json -o ./src/api ``` ### Using Configuration File Create `openapi.config.ts`: ```typescript import { GeneratorConfig } from "ng-openapi"; const config: GeneratorConfig = { input: "./swagger.json", output: "./src/api", options: { dateType: "Date", enumStyle: "enum", }, }; export default config; ``` Then run: ```bash ng-openapi -c openapi.config.ts ``` ## Step 3: Configure Your Angular App Add the provider to your `app.config.ts`: ```typescript import { ApplicationConfig } from "@angular/core"; import { provideHttpClient } from "@angular/common/http"; import { provideDefaultClient } from "./api/providers"; export const appConfig: ApplicationConfig = { providers: [ provideHttpClient(), provideDefaultClient({ basePath: "https://api.example.com", }), ], }; ``` ## Step 4: Use Generated Services ```typescript import { inject } from "@angular/core"; import { toSignal } from "@angular/core/rxjs-interop"; import { PetsService } from "./api/services"; import { Pet } from "./api/models"; export class PetsComponent { private readonly petsService = inject(PetsService); readonly pets = toSignal(this.petsService.listPets()); } ``` ## Generated Structure After generation, you'll have: ``` src/api/ ├── models/ # TypeScript interfaces, enums ├── services/ # One Angular service per controller ├── tokens/ # Injection tokens ├── utils/ # Date transformer, download helpers, … ├── providers.ts # provideDefaultClient() setup function └── index.ts # Main exports ``` See [Generated Output](../guide/generated-code.md) for what every file does. ## Next Steps - [Configuration Reference](../api/configuration.md) — all generator options at a glance - [Angular Integration](../guide/angular-integration.md) — providers, interceptors, environments - [Multiple Clients](../guide/multiple-clients.md) — several APIs in one app - [Schema Validation](../guide/schema-validation.md) — validate responses at runtime --- # Angular Integration Configure ng-openapi providers and services in your Angular application. ## Basic Setup ### Configure Providers ```typescript import { ApplicationConfig } from "@angular/core"; import { provideHttpClient } from "@angular/common/http"; import { provideDefaultClient } from "./client/providers"; export const appConfig: ApplicationConfig = { providers: [ provideHttpClient(), provideDefaultClient({ basePath: "https://api.example.com", }), ], }; ``` ### Inject Services ```typescript import { Component, inject } from "@angular/core"; import { UsersService } from "./client/services"; @Component({ selector: "app-users", template: ``, }) export class UsersComponent { private readonly usersService = inject(UsersService); } ``` ## Environment Configuration ```typescript import { provideDefaultClient } from "./client/providers"; import { environment } from "./environments/environment"; export const appConfig: ApplicationConfig = { providers: [ provideDefaultClient({ basePath: environment.apiUrl, }), ], }; ``` ## Disable Date Transformation ```typescript provideDefaultClient({ basePath: "https://api.example.com", enableDateTransform: false, }); ``` ## Manual Configuration ```typescript import { BASE_PATH } from "./client/tokens"; export const appConfig: ApplicationConfig = { providers: [{ provide: BASE_PATH, useValue: "https://api.example.com" }], }; ``` ## Resources - [Angular Dependency Injection ↗️](https://angular.dev/guide/di) - [Angular Providers ↗️](https://angular.dev/guide/di/dependency-injection-providers) --- # CLI Usage Generate API clients using the ng-openapi command line interface. This page covers day-to-day workflows; the complete flag list lives in the [CLI reference](../api/cli.md). ## Basic Commands ### Direct Generation ```bash ng-openapi -i swagger.json -o ./src/api ``` The input can also be a URL: ```bash ng-openapi -i https://api.example.com/openapi.yaml -o ./src/api ``` ### Configuration File ```bash ng-openapi -c openapi.config.ts ``` ### Generate Subcommand ```bash ng-openapi generate -i swagger.json -o ./src/api ng-openapi gen -c openapi.config.ts # Short alias ``` ## Common Options ### Types Only ```bash ng-openapi -i swagger.json -o ./src/api --types-only ``` ### String Dates ```bash ng-openapi -i swagger.json -o ./src/api --date-type string ``` ## Configuration vs CLI CLI flags cover the quick cases; everything else (headers, plugins, method naming, validation, …) needs a [configuration file](../api/configuration.md): ```typescript // openapi.config.ts import { GeneratorConfig } from "ng-openapi"; const config: GeneratorConfig = { input: "./swagger.json", output: "./src/api", options: { dateType: "Date", enumStyle: "enum", customHeaders: { "X-API-Key": "key" }, responseTypeMapping: { "application/pdf": "blob" }, }, }; export default config; ``` ```bash ng-openapi -c openapi.config.ts ``` ## Workflow Recipes ### Generate Before Serving/Building ```json { "scripts": { "generate:client": "ng-openapi -c openapi.config.ts", "dev": "npm run generate:client && ng serve", "prebuild": "npm run generate:client", "build": "ng build" } } ``` ### Regenerate on Spec Changes ```json { "scripts": { "generate:watch": "nodemon --watch swagger.json --exec 'npm run generate:client'" } } ``` ### Fetch the Spec First ```json { "scripts": { "fetch:spec": "curl https://api.example.com/swagger.json > swagger.json", "generate:client": "npm run fetch:spec && ng-openapi -c openapi.config.ts" } } ``` Alternatively, point `input` directly at the URL and use [`validateInput`](../api/configuration/validate-input.md) to guard against unexpected spec changes. ### Multiple APIs ```json { "scripts": { "generate:users": "ng-openapi -c users-api.config.ts", "generate:orders": "ng-openapi -c orders-api.config.ts", "generate:all": "npm run generate:users && npm run generate:orders" } } ``` See [Multiple Clients](./multiple-clients.md) for the full setup. ## Help and Version ```bash ng-openapi --help ng-openapi generate --help ng-openapi --version ``` ## Resources - [CLI Reference](../api/cli.md) — all flags and defaults - [Configuration Reference](../api/configuration.md) — all config-file properties - [Generated Output](./generated-code.md) — what lands in your output directory --- # Date Handling Work with automatic date transformation features in ng-openapi. ## Automatic Date Transformation ### Configuration ```typescript // openapi.config.ts const config: GeneratorConfig = { options: { dateType: "Date", // Enables automatic transformation }, }; ``` ### Generated Models ```typescript // Generated interface with Date type interface User { id: number; name: string; createdAt: Date; // Automatically transformed from ISO string updatedAt: Date; } ``` ### Usage ```typescript export class UsersComponent { private readonly usersService = inject(UsersService); loadUser(id: number) { this.usersService.getUserById(id).subscribe((user) => { // createdAt is already a Date object console.log(user.createdAt.getFullYear()); console.log(user.createdAt.toLocaleDateString()); }); } } ``` ## String Dates ### Configuration ```typescript // openapi.config.ts const config: GeneratorConfig = { options: { dateType: "string", // No transformation }, }; ``` ### Generated Models ```typescript // Generated interface with string type interface User { id: number; name: string; createdAt: string; // ISO string format updatedAt: string; } ``` ### Usage ```typescript export class UsersComponent { loadUser(id: number) { this.usersService.getUserById(id).subscribe((user) => { // Convert manually when needed const createdDate = new Date(user.createdAt); console.log(createdDate.getFullYear()); }); } } ``` ## Date Transformer Interceptor ### Disable Transformation ```typescript // Disable in provider provideDefaultClient({ basePath: "https://api.example.com", enableDateTransform: false, }); ``` ### Manual Setup `DateInterceptor` is class-based, so it is registered through the `HTTP_INTERCEPTORS` multi-provider (`withInterceptors` only accepts functional interceptors): ```typescript import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi } from "@angular/common/http"; import { DateInterceptor } from "./client/utils/date-transformer"; export const appConfig: ApplicationConfig = { providers: [ provideHttpClient(withInterceptorsFromDi()), { provide: HTTP_INTERCEPTORS, useClass: DateInterceptor, multi: true }, ], }; ``` ## Which Strings Are Detected as Dates? The interceptor only converts strings matching a strict full ISO 8601 date-time pattern (`ISO_DATE_REGEX`), so bare years or numeric IDs are never accidentally turned into `Date` objects. The exact pattern and all recognized formats are documented in the [Date Transformer reference](../api/utilities/date-transformer.md#recognized-formats). ### Custom Date Regex If your API returns a format the default pattern doesn't cover, pass your own regex via the provider — no need to copy `date-transformer.ts`: ```typescript provideDefaultClient({ basePath: "https://api.example.com", // Use any pattern you like; it overrides ISO_DATE_REGEX dateTransformRegex: /your-custom-pattern/, }); ``` `transformDates` and `DateInterceptor` also accept the regex directly, so you can reuse them in a manual interceptor setup: ```typescript new DateInterceptor(/your-custom-regex/); transformDates(responseBody, /your-custom-regex/); ``` ## Resources - [Date Transformer reference](../api/utilities/date-transformer.md) — recognized formats, generated source, manual setup - [JavaScript Date ↗️](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) - [Angular HTTP Interceptors ↗️](https://angular.dev/guide/http/interceptors) --- # File Downloads Use the built-in file download utilities with generated services. ## Basic Usage ### downloadFileOperator ```typescript import { Component, inject } from "@angular/core"; import { downloadFileOperator } from "./client/utils/file-download"; import { ReportsService } from "./client/services"; export class ReportsComponent { private readonly reportsService = inject(ReportsService); downloadReport(reportId: number) { this.reportsService.getReportPdf(reportId).pipe(downloadFileOperator("report.pdf")).subscribe(); } } ``` ### Dynamic Filenames ```typescript downloadReport(reportId: number) { this.reportsService.getReportPdf(reportId) .pipe( downloadFileOperator(`report-${reportId}.pdf`) ) .subscribe(); } ``` ### Function-Based Filenames ```typescript downloadReport(reportId: number) { this.reportsService.getReportPdf(reportId) .pipe( downloadFileOperator((blob: Blob) => { const date = new Date().toISOString().split('T')[0]; return `report-${reportId}-${date}.pdf`; }) ) .subscribe(); } ``` ## Extract Filename from Headers ### extractFilenameFromContentDisposition ```typescript import { HttpClient, HttpResponse } from "@angular/common/http"; import { extractFilenameFromContentDisposition, downloadFile } from "./client/utils/file-download"; export class FilesService { private readonly http = inject(HttpClient); downloadWithHeaders(url: string) { this.http .get(url, { responseType: "blob", observe: "response", }) .subscribe((response: HttpResponse) => { const contentDisposition = response.headers.get("Content-Disposition"); const filename = extractFilenameFromContentDisposition(contentDisposition, "download.pdf"); if (response.body) { downloadFile(response.body, filename); } }); } } ``` ## Direct Download Function ### downloadFile ```typescript import { downloadFile } from "./client/utils/file-download"; // Direct usage without operator this.reportsService.getReportPdf(reportId).subscribe((blob) => { downloadFile(blob, "report.pdf"); }); ``` ## Response Type Mapping Configure blob responses in your OpenAPI config: ```typescript // openapi.config.ts const config: GeneratorConfig = { options: { responseTypeMapping: { "application/pdf": "blob", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "blob", }, }, }; ``` ## Resources - [RxJS Operators ↗️](https://rxjs.dev/guide/operators) - [Angular HTTP Client ↗️](https://angular.dev/guide/http) --- # Generated Output What ng-openapi actually writes into your output directory, file by file. This is the canonical reference for the generated structure — other pages link here instead of repeating it. ## The Full Tree With the default configuration (`generateServices: true`, `dateType: "Date"`): ``` / ├── models/ │ ├── index.ts # TypeScript interfaces, enums / unions │ ├── *.ts # One file per schema (only with modelFileStructure: 'per-type') │ └── request-params.ts # Request-object interfaces (only with useSingleRequestParameter) ├── services/ │ ├── index.ts # Service exports │ └── *.service.ts # One Angular service per controller/tag ├── tokens/ │ └── index.ts # Injection tokens for this client ├── utils/ │ ├── base-interceptor.ts # Routes client-scoped interceptors │ ├── date-transformer.ts # Date interceptor (only with dateType: "Date") │ ├── file-download.ts # Download helpers │ └── http-params-builder.ts # Query-param serialization ├── providers.ts # provideClient() setup function └── index.ts # Main barrel export ``` Plugins add their own directories next to these: - `validators/` — Zod schemas, one file per controller ([Zod plugin](../api/configuration/plugins/zod.md)) - `resources/` — `httpResource`-based services ([HTTP Resource plugin](../api/configuration/plugins/http-resource.md)) With [`generateServices: false`](../api/configuration/options/generate-services.md) only `models/` and the main `index.ts` are generated. ## File by File ### `models/index.ts` One interface per schema in the spec, plus enums in the style you chose via [`enumStyle`](../api/configuration/options/enum-style.md). Date fields are typed `Date` or `string` depending on [`dateType`](../api/configuration/options/date-type.md). Type names can be decorated with a prefix/suffix via [`naming.models`](../api/configuration/options/naming.md). With [`modelFileStructure: 'per-type'`](../api/configuration/options/model-file-structure.md), each schema instead gets its own `models/.ts` file (plus `models/request-options.ts` for the `RequestOptions` interface), and `models/index.ts` becomes a pure barrel re-exporting them — imports from `../models` and the main `index.ts` are unaffected. ### `models/request-params.ts` Only generated with [`useSingleRequestParameter`](../api/configuration/options/use-single-request-parameter.md): one exported `Params` interface per operation, re-exported through the `models` barrel. ### `services/*.service.ts` One injectable service per controller (OpenAPI tag), using `inject(HttpClient)` and this client's base-path token. Class names default to `Service` and can be decorated via [`naming.services`](../api/configuration/options/naming.md) (file names are unaffected). Classes are decorated with `@Injectable({ providedIn: "root" })`, or Angular 22+'s `@Service()` when [`serviceDecorator`](../api/configuration/options/service-decorator.md) is set to `'service'`. Method names come from `operationId`, optionally transformed by [`customizeMethodName`](../api/configuration/options/customize-method-name.md). When [`validation.response`](../api/configuration/options/validation.md) is enabled, each method accepts a `parse` hook in its trailing options parameter. ### `tokens/index.ts` Injection tokens namespaced per client so multiple clients can coexist (see [Multiple Clients](./multiple-clients.md)): - `BASE_PATH_` — the API base URL (falls back to `/api`) - `HTTP_INTERCEPTORS_` — this client's interceptor instances - `CLIENT_CONTEXT_TOKEN_` — `HttpContext` token marking which client a request belongs to For the default client, deprecated `BASE_PATH` / `CLIENT_CONTEXT_TOKEN` aliases are kept for backwards compatibility. ### `utils/base-interceptor.ts` A global interceptor that checks each request's `HttpContext` and applies this client's interceptor chain only to requests made by this client's services — that's what keeps interceptors from leaking across clients. ### `utils/date-transformer.ts` Only generated with `dateType: "Date"`. Contains `ISO_DATE_REGEX`, `transformDates`, and the `DateInterceptor` that converts ISO date strings in responses to `Date` objects. See the [Date Transformer reference](../api/utilities/date-transformer.md). ### `utils/file-download.ts` `downloadFile`, `downloadFileOperator`, and `extractFilenameFromContentDisposition` for handling blob downloads. See the [File Download Helper reference](../api/utilities/file-download-helper.md). ### `utils/http-params-builder.ts` Serializes query parameters into `HttpParams`, handling arrays, nested objects, and `Date` values. Used internally by the generated services. ### `providers.ts` The `provideClient()` function (e.g. `provideDefaultClient`) plus its config interface. Wires up the base-path token, the base interceptor, client-scoped interceptors, and (with `dateType: "Date"`) the date interceptor. See the [Providers reference](../api/providers.md). ### `index.ts` Barrel export of everything above, so consumers can import from the output root. ## Regeneration Notes - Every file starts with a "Generated by ng-openapi — do not edit" header; regeneration overwrites them, so put customizations in your own code (interceptors, wrappers), never in the output directory. - Generated files carry `@ts-nocheck` and `eslint-disable` pragmas so they don't fight your project's lint/strict settings. - Add the output directory to your API-generation script rather than committing manual tweaks — see [CLI Usage](./cli-usage.md) for workflow recipes. --- # Guides Learn how to use ng-openapi features effectively in your Angular applications. New here? Start with the [Quick Start](../getting-started/quick-start.md). ## Setup ### [CLI Usage](./cli-usage.md) Everyday generation workflows: npm scripts, watch mode, fetching specs, multiple APIs. ### [Angular Integration](./angular-integration.md) Configure ng-openapi providers and services in your Angular application. ### [Generated Output](./generated-code.md) A file-by-file tour of everything ng-openapi writes into your output directory. ## Features ### [Multiple Clients](./multiple-clients.md) Configure multiple API clients in a single Angular application. ### [Date Handling](./date-handling.md) Work with automatic date transformation features. ### [File Downloads](./file-download.md) Use the built-in file download utilities with generated services. ### [Schema Validation](./schema-validation.md) Validate API responses against the OpenAPI schema at runtime. ## Plugins ### [HTTP Resource Plugin](./http-resource.md) Generate Angular services using the `httpResource` API. ### [Plugin Authoring](./plugin-authoring.md) Write your own generator plugin against the documented plugin contract. --- # HTTP Resource Plugin Generate Angular services using the `httpResource` API for automatic caching, state management, and reactive data loading. ## Overview The HTTP Resource plugin extends ng-openapi to generate services that leverage Angular's new `httpResource` API instead of traditional `HttpClient`. This provides built-in caching, loading states, error handling, and reactive updates through Angular Signals. ## Installation Install the plugin alongside ng-openapi: ```bash npm install ng-openapi @ng-openapi/http-resource --save-dev ``` ## Configuration Add the plugin to your OpenAPI configuration: ```typescript // openapi.config.ts import { GeneratorConfig } from "ng-openapi"; import { HttpResourcePlugin } from "@ng-openapi/http-resource"; export default { input: "./swagger.json", output: "./src/api", clientName: "MyApi", plugins: [HttpResourcePlugin], options: { dateType: "Date", enumStyle: "enum", }, } as GeneratorConfig; ``` ## Generation Generate your API resources: ```bash ng-openapi -c openapi.config.ts ``` This creates both traditional services and HTTP resource services: ``` src/api/ ├── models/ # TypeScript interfaces ├── services/ # Traditional HttpClient services ├── resources/ # HTTP Resource services │ ├── index.ts # Resource exports │ └── *.resource.ts # Generated resources ├── providers.ts # Provider functions └── index.ts # Main exports ``` ## Provider Setup Configure the provider in your application: ```typescript // app.config.ts import { ApplicationConfig } from "@angular/core"; import { provideMyApiClient } from "./api/providers"; export const appConfig: ApplicationConfig = { providers: [ provideMyApiClient({ basePath: "https://api.example.com", }), ], }; ``` ## Basic Usage Inject and use the generated resources in your components: ```typescript import { Component, inject } from "@angular/core"; import { UsersResource } from "./api/resources"; @Component({ selector: "app-users", template: `
@if (users.isLoading()) {

Loading users...

} @else if (users.error()) {

Error: {{ users.error()?.message }}

} @else { @for (user of users.value(); track user.id) {
{{ user.name }}
} }
`, }) export class UsersComponent { private readonly usersResource = inject(UsersResource); readonly users = this.usersResource.getUsers(); } ``` ## Dynamic Parameters Use Signals for reactive parameter binding: ```typescript export class UserDetailComponent { private readonly usersResource = inject(UsersResource); private readonly userId = signal(1); // Automatically refetches when userId changes readonly user = this.usersResource.getUserById(this.userId); updateUser(newId: number) { this.userId.set(newId); // Triggers automatic refetch } } ``` ## Default Values Provide fallback values while data is loading: ```typescript export class UsersComponent { private readonly usersResource = inject(UsersResource); readonly users = this.usersResource.getUsers({ defaultValue: [] }); } ``` ## Query Parameters Pass both static and reactive query parameters: ```typescript export class SearchComponent { private readonly usersResource = inject(UsersResource); private readonly searchTerm = signal(""); private readonly pageSize = signal(10); readonly searchResults = this.usersResource.searchUsers( this.searchTerm, // reactive search term this.pageSize, // reactive page size "active", // static status filter ); updateSearch(term: string) { this.searchTerm.set(term); } } ``` ## Resource vs Service Comparison | Feature | HTTP Resource | Traditional Service | | ------------------------- | ------------------------- | -------------------------- | | **Loading State** | ✅ Built-in `isLoading()` | ❌ Manual state management | | **Error Handling** | ✅ Built-in `error()` | ❌ Manual error handling | | **Reactivity** | ✅ Signal-based | ❌ Observable-based | | **Parameter Binding** | ✅ Signal or static | ❌ Manual subscription | | **Request Deduplication** | ✅ Automatic | ❌ Manual implementation | | **Maturity** | ✅ Stable | ✅ Stable | ## Limitations - **GET Requests Only**: Currently optimized for "GET" requests (see [Angular Docs ↗️](https://angular.dev/guide/http/http-resource)) ## Resources - [Angular httpResource Documentation ↗️](https://angular.dev/guide/http/resource) - [Angular Signals Guide ↗️](https://angular.dev/guide/signals) - [ng-openapi Configuration](../api/configuration.md) --- # Multiple Clients Configure multiple API clients in a single Angular application with independent configurations and interceptors. ## Overview ng-openapi supports generating multiple clients for different APIs, each with their own base paths, interceptors, and configurations. This is useful when your application needs to communicate with multiple backend services. ## Generating Multiple Clients Create separate configuration files for each API: ```typescript // users-api.config.ts import { GeneratorConfig } from "ng-openapi"; const config: GeneratorConfig = { clientName: "Users", input: "./users-swagger.json", output: "./src/api/users", options: { dateType: "Date", enumStyle: "enum", }, }; export default config; ``` ```typescript // orders-api.config.ts import { GeneratorConfig } from "ng-openapi"; const config: GeneratorConfig = { clientName: "Orders", input: "./orders-swagger.json", output: "./src/api/orders", options: { dateType: "Date", enumStyle: "enum", }, }; export default config; ``` Generate each client: ```bash ng-openapi -c users-api.config.ts ng-openapi -c orders-api.config.ts ``` ## Provider Configuration Each client generates its own provider function based on the `clientName`: ```typescript // app.config.ts import { ApplicationConfig } from "@angular/core"; import { provideHttpClient } from "@angular/common/http"; import { provideUsersClient } from "./api/users/providers"; import { provideOrdersClient } from "./api/orders/providers"; export const appConfig: ApplicationConfig = { providers: [ provideHttpClient(), provideUsersClient({ basePath: "https://users-api.example.com", }), provideOrdersClient({ basePath: "https://orders-api.example.com", }), ], }; ``` ## Independent Interceptors Apply different interceptors to each client: ```typescript // auth.interceptor.ts import { HttpInterceptor, HttpRequest, HttpHandler } from "@angular/common/http"; import { Injectable } from "@angular/core"; @Injectable() export class AuthInterceptor implements HttpInterceptor { intercept(req: HttpRequest, next: HttpHandler) { const authReq = req.clone({ headers: req.headers.set("Authorization", "Bearer token"), }); return next.handle(authReq); } } ``` ```typescript // logging.interceptor.ts import { HttpInterceptor, HttpRequest, HttpHandler } from "@angular/common/http"; import { Injectable } from "@angular/core"; @Injectable() export class LoggingInterceptor implements HttpInterceptor { intercept(req: HttpRequest, next: HttpHandler) { console.log("Request:", req.url); return next.handle(req); } } ``` Configure interceptors per client: ```typescript export const appConfig: ApplicationConfig = { providers: [ provideHttpClient(), provideUsersClient({ basePath: "https://users-api.example.com", interceptors: [AuthInterceptor], // Only for users API }), provideOrdersClient({ basePath: "https://orders-api.example.com", interceptors: [AuthInterceptor, LoggingInterceptor], // Both interceptors }), ], }; ``` ## Using Multiple Clients Import and use services from different clients: ```typescript import { Component, inject } from "@angular/core"; import { toSignal } from "@angular/core/rxjs-interop"; import { UsersService } from "./api/users/services"; import { OrdersService } from "./api/orders/services"; @Component({ selector: "app-dashboard", template: `

Users: {{ users()?.length ?? 0 }}

Orders: {{ orders()?.length ?? 0 }}

`, }) export class DashboardComponent { private readonly usersService = inject(UsersService); private readonly ordersService = inject(OrdersService); readonly users = toSignal(this.usersService.getUsers()); readonly orders = toSignal(this.ordersService.getOrders()); } ``` ## Client Isolation Each client operates independently: - **Separate Base Paths** - Different API endpoints - **Independent Interceptors** - Apply different authentication, logging, or transformation logic - **Isolated Configuration** - Different date handling, headers, or response types - **No Cross-Client Interference** - Changes to one client don't affect others ## Generated Structure With multiple clients, your project structure looks like: ``` src/ ├── api/ │ ├── users/ │ │ ├── models/ │ │ ├── services/ │ │ ├── tokens/ │ │ ├── utils/ │ │ ├── providers.ts │ │ └── index.ts │ └── orders/ │ ├── models/ │ ├── services/ │ ├── tokens/ │ ├── utils/ │ ├── providers.ts │ └── index.ts └── app/ └── app.config.ts ``` ## Package.json Scripts Organize generation scripts for multiple clients: ```json { "scripts": { "generate:users": "ng-openapi -c users-api.config.ts", "generate:orders": "ng-openapi -c orders-api.config.ts", "generate:all": "npm run generate:users && npm run generate:orders" } } ``` ## Best Practices ### Naming Convention Use descriptive client names that reflect the API purpose: ```typescript clientName: "Users"; // generates provideUsersClient clientName: "Orders"; // generates provideOrdersClient clientName: "Payments"; // generates providePaymentsClient ``` ### Directory Organization Keep clients in separate directories: ```bash ng-openapi -c users-api.config.ts # outputs to ./src/api/users ng-openapi -c orders-api.config.ts # outputs to ./src/api/orders ``` ### Environment Configuration Use environment-specific configurations: ```typescript import { environment } from "../environments/environment"; export const appConfig: ApplicationConfig = { providers: [ provideUsersClient({ basePath: environment.usersApiUrl, }), provideOrdersClient({ basePath: environment.ordersApiUrl, }), ], }; ``` ## Resources - [Provider Configuration](../api/providers.md) - [CLI Usage](./cli-usage.md) - [Angular Dependency Injection ↗️](https://angular.dev/guide/di) --- # Plugin Authoring ng-openapi plugins are generator classes that run after the core type/service generation and emit additional files into the same output directory. The built-in [HttpResourcePlugin](../api/configuration/plugins/http-resource.md) and [ZodPlugin](../api/configuration/plugins/zod.md) are implemented against the exact contract described here — a third-party plugin needs nothing beyond the public `ng-openapi` API. ## The contract A plugin is a class implementing `IPluginGenerator`, constructed by the orchestrator with a single `PluginGeneratorContext` argument: ```typescript import { IPluginGenerator, PluginGeneratorContext } from "ng-openapi"; import * as path from "path"; export class MyPlugin implements IPluginGenerator { private readonly context: PluginGeneratorContext; constructor(context: PluginGeneratorContext) { this.context = context; } async generate(outputRoot: string): Promise { const { spec, project, onWarning } = this.context; if (spec.operations.length === 0) { onWarning?.("Nothing to generate: the specification has no operations"); return; } const file = project.createSourceFile(path.join(outputRoot, "my-plugin", "index.ts"), "", { overwrite: true, }); // ... build the file from spec.operations / spec.definitions ... file.formatText(); file.saveSync(); } } ``` Users register the class in their config: ```typescript export default { // ... plugins: [MyPlugin], } as GeneratorConfig; ``` ## What the context provides | Field | Type | Notes | |---|---|---| | `spec` | `NormalizedSpec` | The version-free spec model. `$ref`s are resolved and per-operation fields (`pathParams`, `queryParams`, `hasBody`, `isMultipart`, `responseType`, …) are precomputed. Plugins never see Swagger 2.0 vs OpenAPI 3.x differences. | | `project` | `Project` (ts-morph) | The shared project every generator emits through. Create files via `project.createSourceFile(...)` so the orchestrator can report them in `GenerationResult.filesWritten`. | | `config` | `GeneratorConfig` | The full user-facing config. Read only the slice you need (e.g. `config.clientName`, `config.options.dateType`). | | `onWarning` | `(message: string) => void` (optional) | Sink for non-fatal diagnostics. Never `console.*` from a plugin — warnings surface on `GenerationResult.warnings` and through the CLI's reporter. | ## Rules of engagement - **Consume `NormalizedSpec`, not the raw spec.** All version quirks are resolved at parse time; if something you need is missing from the model, that is a gap to raise upstream, not a reason to re-parse the input. - **Never log.** The core is silent by design; the CLI owns presentation. Report problems through `onWarning` or by throwing an `Error` (which aborts generation with a clean CLI message). - **Emit through the shared `project`.** Files written behind its back won't be tracked, formatted consistently, or visible to `fixMissingImports()`. - **Validation is done for you.** By the time a plugin is constructed, the spec has been parsed, validated, and normalized — no need for your own guards. --- # Schema Validation Validate your API responses against the OpenAPI schema at runtime using any validation library, such as `zod`, `valibot`, `ajv`...etc. ## Overview `httpResource` supports it natively using the `parse` option in its [options ↗️](https://angular.dev/guide/http/http-resource#response-parsing-and-validation). The same option is now available for the generated `HttpClient` service methods. It works the same way, but it doesn't allow you to transform the response data. ## Example using Zod Enable the response validation: ```typescript // users-api.config.ts import { GeneratorConfig } from "ng-openapi"; const config: GeneratorConfig = { clientName: "PetStore", input: "https://petstore3.swagger.io/api/v3/openapi.json", output: "./generated", options: { dateType: "Date", enumStyle: "enum", validation: { response: true, }, }, }; export default config; ``` In your component use your zod schema in the `parse` option: ```typescript // zod schema for the response const getPetByIdResponse = z.object({ id: z.number().optional(), name: z.string(), category: z .object({ id: z.number().optional(), name: z.string().optional(), }) .optional(), photoUrls: z.array(z.string()), tags: z .array( z.object({ id: z.number().optional(), name: z.string().optional(), }), ) .optional(), status: z.enum(["available", "pending", "sold"]).optional().describe("pet status in the store"), }); @Component({ templateUrl: "./example-view.html", }) export class ExampleView { readonly #petService = inject(PetService); readonly availablePets = toSignal( this.#petService.getPetById(1, undefined, { parse: getPetByIdResponse.parse, // validate the response using zod }), ); } ``` --- # CLI Generate Angular services and TypeScript types from OpenAPI specifications using the command line interface. ## Usage ```bash ng-openapi [command] [options] ``` ## Commands ### Direct Generation ```bash ng-openapi -i swagger.json -o ./src/api ``` ### Configuration File ```bash ng-openapi -c openapi.config.ts ``` ### Generate Subcommand ```bash ng-openapi generate -i swagger.json -o ./src/api ng-openapi gen -c openapi.config.ts # Short alias ``` ## Options ### Source Options (one required) Provide either a configuration file or a spec directly: | Option | Alias | Description | Example | | ---------- | ----- | ------------------------------------ | ---------------------- | | `--config` | `-c` | Path to configuration file | `-c openapi.config.ts` | | `--input` | `-i` | Path or URL to OpenAPI specification | `-i swagger.json` | ### Output Options | Option | Alias | Description | Default | Example | | ---------- | ----- | ---------------- | ----------------- | -------------- | | `--output` | `-o` | Output directory | `./src/generated` | `-o ./src/api` | ### Generation Options | Option | Description | Default | Example | | -------------- | ----------------------------------- | ------- | -------------------- | | `--types-only` | Generate only TypeScript interfaces | `false` | `--types-only` | | `--date-type` | Date type to use | `Date` | `--date-type string` | ### Help and Version | Option | Description | | ----------- | --------------------- | | `--help` | Show help information | | `--version` | Show version number | ## Examples ```bash # Generate from local file ng-openapi -i ./swagger.json -o ./src/api # Generate only types ng-openapi -i swagger.json -o ./src/api --types-only # Use string for dates ng-openapi -i swagger.json -o ./src/api --date-type string # Use configuration file ng-openapi -c openapi.config.ts # Generate with subcommand ng-openapi generate -i swagger.json -o ./src/api ``` --- # Configuration Extensive configuration options to customize the generated output to match your needs. ## Usage The `defineConfig` helper gives you full autocomplete and inline type errors without a manual annotation: ```typescript // openapi.config.ts import { defineConfig } from "ng-openapi"; export default defineConfig({ input: "./swagger.json", output: "./src/api", options: { dateType: "Date", enumStyle: "enum", }, }); ``` Annotating a plain object with the `GeneratorConfig` type works identically (and is the way to go on ng-openapi versions that don't ship `defineConfig` yet): ```typescript // openapi.config.ts import { GeneratorConfig } from "ng-openapi"; const config: GeneratorConfig = { input: "./swagger.json", output: "./src/api", options: { dateType: "Date", enumStyle: "enum", }, }; export default config; ``` ## Properties at a Glance | Property | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | [`input`](configuration/input.md) | `string` | ✅ | — | Path or URL of the OpenAPI/Swagger spec (`.json`, `.yaml`, `.yml`) | | [`output`](configuration/output.md) | `string` | ✅ | — | Output directory for generated files | | [`options`](configuration/options.md) | `object` | ✅ | — | Generation options — see [options overview](configuration/options.md) | | [`clientName`](configuration/client-name.md) | `string` | — | `'default'` | Names the provider function and tokens; enables multiple clients per app | | [`validateInput`](configuration/validate-input.md) | `(spec) => boolean` | — | `undefined` | Acceptance check on the parsed spec; `false` aborts generation | | [`plugins`](configuration/plugins.md) | `IPluginGeneratorClass[]` | — | `undefined` | Plugin generators run after core generation | | [`compilerOptions`](configuration/compiler-options.md) | `object` | — | `undefined` | ts-morph compiler settings for generation | ## Configuration Properties ### [Input](configuration/input.md) **Type:** `string` | **Required** Path or http(s) URL of your OpenAPI/Swagger specification. ### [Output](configuration/output.md) **Type:** `string` | **Required** Output directory for generated files. ### [Options](configuration/options.md) **Type:** `object` | **Required** Object containing various options to customize the code generation process. ### [Client Name](configuration/client-name.md) **Type:** `string | undefined` | **Default:** `'default'` Unique identifier for this client. Names the generated provider function (`provideClient`) and injection tokens, so multiple clients can coexist in one application. ### [Validate Input](configuration/validate-input.md) **Type:** `(spec: SwaggerSpec) => boolean | undefined` | **Default:** `undefined` Custom acceptance check run on the parsed specification; returning `false` aborts generation. ### [Plugins](configuration/plugins.md) **Type:** `IPluginGeneratorClass[] | undefined` | **Default:** `undefined` Plugin generator classes (e.g. `HttpResourcePlugin`, `ZodPlugin`), run after core generation. ### [Compiler Options](configuration/compiler-options.md) **Type:** `object | undefined` | **Default:** `undefined` TypeScript compiler options for the generated code. --- # `clientName` **Type:** `string | undefined` | **Default:** `Default` Unique identifier for the generated client code. This is used to differentiate between multiple clients in the same project. ## Usage ```typescript // openapi.config.ts import { GeneratorConfig } from 'ng-openapi'; const config: GeneratorConfig = { clientName: 'PetStore', ... // other configurations }; export default config; ``` ## Notes - The generated [provider](../providers.md) will be named `provideClient`. Which then can be used in the `app.config.ts` file. --- # `compilerOptions` **Type:** `object | undefined` | **Default:** `undefined` TypeScript compiler options for the generated code. ## Usage ```typescript // openapi.config.ts import { GeneratorConfig } from 'ng-openapi'; const config: GeneratorConfig = { compilerOptions: { declaration: true, target: ScriptTarget.ES2022, module: ModuleKind.Preserve, strict: true }, ... // other configurations }; export default config; ``` ## Schema ```typescript type CompilerOptions = { declaration?: boolean; target?: ScriptTarget; module?: ModuleKind; strict?: boolean; }; ``` ## Notes - When not specified, the generator uses default Angular TypeScript compiler settings --- # `input` **Type:** `string` | **Required** Path or http(s) URL of your OpenAPI/Swagger specification. ## Usage ```typescript // openapi.config.ts import { GeneratorConfig } from 'ng-openapi'; const config: GeneratorConfig = { input: './swagger.json', ... // other configurations }; export default config; ``` Remote specifications work the same way: ```typescript const config: GeneratorConfig = { input: 'https://api.example.com/openapi.yaml', ... // other configurations }; ``` ## Supported Formats - **JSON**: `.json` files containing OpenAPI/Swagger specifications - **YAML**: `.yaml` / `.yml` files containing OpenAPI/Swagger specifications - **URLs**: `http(s)` URLs returning any of the above ## Notes - The specification must be a valid Swagger 2.x or OpenAPI 3.x document - Remote URLs must be accessible and return valid JSON/YAML content - Consider [`validateInput`](./validate-input.md) to guard against unexpected spec changes when generating from a URL --- # `options` **Type:** `object` | **Required** Object containing various options to customize the code generation process. ## Usage ```typescript // openapi.config.ts import { GeneratorConfig } from 'ng-openapi'; const config: GeneratorConfig = { options: { dateType: 'Date', enumStyle: 'enum', generateServices: true, generateEnumBasedOnDescription: false, customHeaders: { 'Accept': 'application/json', ... // other headers }, responseTypeMapping: { 'application/pdf': 'blob', ... // other mappings }, ... // other configurations } }; export default config; ``` ## Options at a Glance | Option | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | [`dateType`](options/date-type) | `'string' \| 'Date'` | ✅ | — | How date/date-time fields are typed (and whether the date interceptor is generated) | | [`enumStyle`](options/enum-style) | `'enum' \| 'union'` | ✅ | — | Emit TypeScript enums or literal-union types | | [`generateServices`](options/generate-services) | `boolean` | — | `true` | Set `false` to generate models only | | [`validation`](options/validation) | `{ response?: boolean }` | — | `undefined` | Adds a `parse` hook to service methods for runtime response validation | | [`generateEnumBasedOnDescription`](options/generate-enums-description) | `boolean` | — | `false` | Read enum member names from JSON-encoded descriptions | | [`customHeaders`](options/custom-headers) | `Record` | — | `undefined` | Default headers added to every request | | [`emitAcceptHeader`](options/emit-accept-header) | `boolean` | — | `true` | Send an `Accept` header derived from each operation's response content types | | [`responseTypeMapping`](options/response-type-mapping) | `Record` | — | `undefined` | Pin the Angular `responseType` per content type | | [`customizeMethodName`](options/customize-method-name) | `(operationId) => string` | — | `undefined` | Derive method names from `operationId`s | | [`useSingleRequestParameter`](options/use-single-request-parameter) | `boolean` | — | `false` | One request object per method instead of positional parameters | | [`serviceDecorator`](options/service-decorator) | `'injectable' \| 'service'` | — | `'injectable'` | Emit Angular 22+'s `@Service()` instead of `@Injectable({ providedIn: 'root' })` | | [`naming`](options/naming) | `NamingOptions` | — | `undefined` | Prefix/suffix decoration of generated service, resource and model identifiers | | [`modelFileStructure`](options/model-file-structure) | `'single' \| 'per-type'` | — | `'single'` | Keep all models in one `models/index.ts` or write one file per schema | --- # `customHeaders` **Type:** `Record | undefined` | **Default:** `undefined` Default headers to be included in all HTTP requests made by the generated services. :::tip We recommend using the `HttpInterceptor` for setting headers — it's cleaner, more maintainable and recommended by the Angular Dev Community. [Learn more in Angular Docs ↗️](https://angular.dev/api/common/http/HttpInterceptor) ::: ## Usage ```typescript // openapi.config.ts import { GeneratorConfig } from 'ng-openapi'; const config: GeneratorConfig = { options: { customHeaders: { 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'application/json' }, }, ... // other configurations }; export default config; ``` --- # `customizeMethodName` **Type:** `Function | undefined` | **Default:** `undefined` **Signature:** `(operationId: string) => string` Provides a custom function to modify how method names are generated based on the `operationId` from the OpenAPI specification. ## Usage ```typescript // openapi.config.ts import { GeneratorConfig } from 'ng-openapi'; const config: GeneratorConfig = { options: { customizeMethodName: (operationId: string) => { const methodName = operationId.split('_').pop() ?? operationId; return methodName.charAt(0).toLowerCase() + methodName.slice(1); } }, ... // other configurations }; export default config; ``` Given an OpenAPI spec like this: ```json { "/api/pets/{id}": { "get": { "tags": ["Pets"], "operationId": "Pets_GetPetById" ... // other properties } } } ``` This generates a method named `getPetById` in the `PetsService` instead of the default `Pets_GetPetById`. ## Notes - `OperationId`s must be unique across the OpenAPI specification - Usually includes the controller name and action name - The customization function allows you to modify this to fit your naming conventions --- # `dateType` **Type:** `string` | **Required** Specifies how to handle date/datetime fields in the generated code. ## Usage ```typescript // openapi.config.ts import { GeneratorConfig } from 'ng-openapi'; const config: GeneratorConfig = { options: { dateType: 'Date' // or 'string' }, ... // other configurations }; export default config; ``` ## Supported Options ### `'Date'` (Default) Generates date objects with [automatic transformation](../../utilities/date-transformer.md) for date and datetime fields. ```typescript interface Event { id: number; name: string; date: Date; // Automatically transformed to Date object } ``` ### `'string'` Generates string types for date and datetime fields without any transformation. ```typescript interface Event { id: number; name: string; date: string; // No transformation, just a string } ``` ## Notes - Using `'Date'` generates an HTTP Interceptor that automatically transforms date strings to `Date` objects - The interceptor is included by default unless disabled in the [provider configuration](../../providers) --- # `emitAcceptHeader` **Type:** `boolean | undefined` | **Default:** `true` Sends an `Accept` header derived from each operation's response content types, so servers that use content negotiation (for example versioned vendor media types like `application/vnd.users+json;version=1.0`) return the representation the generated method is prepared to parse. The header value contains the content types declared on the operation's first success response that agree with the method's Angular `responseType` — advertising a type the method could not parse is deliberately avoided. Operations whose success response declares no content (for example `204 No Content`) send no `Accept` header. The generated code only sets the header when it is not already present, so values from [`customHeaders`](custom-headers) or per-request options always win: ```typescript // generated if (!headers.has('Accept')) { headers = headers.set('Accept', 'application/vnd.users+json;version=1.0'); } ``` Set `emitAcceptHeader: false` to restore the previous behavior of sending no `Accept` header. ## Usage ```typescript // openapi.config.ts import { GeneratorConfig } from 'ng-openapi'; const config: GeneratorConfig = { options: { emitAcceptHeader: false, // opt out of the spec-derived Accept header }, ... // other configurations }; export default config; ``` --- # `enumStyle` **Type:** `string` | **Required** Specifies how to generate enum types in the generated code. Can be either a TypeScript `enum` or a union type. ## Usage ```typescript // openapi.config.ts import { GeneratorConfig } from 'ng-openapi'; const config: GeneratorConfig = { options: { enumStyle: 'enum' // or 'union' }, ... // other configurations }; export default config; ``` ## Supported Options ### `'enum'` (Default) Generates TypeScript `enum` types for enumerations. ```typescript // Example enum with integer values enum Status { _0 = 0, _1 = 1, } // Example enum with string values enum Status { Active = "active", Inactive = "inactive", } ``` ### `'union'` Generates a literal-union type alias plus a same-named `const` object, so values can be both type-checked and referenced like enum members. ```typescript export type Status = 'active' | 'inactive'; export const Status = { Active: 'active' as Status, Inactive: 'inactive' as Status, }; ``` ## Notes - OpenAPI only stores enum values, not names. The generator creates TypeScript enums with names based on values - If your OpenAPI spec contains a description for the `enum` object, `ng-openapi` can generate [enums based on that description](generate-enums-description) --- # `generateEnumBasedOnDescription` **Type:** `boolean | undefined` | **Default:** `false` When set to `true`, the generator parses enum values from the description field of the OpenAPI specification for more descriptive enum names. ## Usage ```typescript // openapi.config.ts import { GeneratorConfig } from 'ng-openapi'; const config: GeneratorConfig = { options: { enumStyle: 'enum', generateEnumBasedOnDescription: true, }, ... // other configurations }; export default config; ``` ## Description Format The generator expects the description to be a JSON string of `EnumValueObject[]`: ```typescript interface EnumValueObject { Name: string; Value: number; } ``` ### Example OpenAPI Enum with Description ```json { "Status": { "enum": [0, 1], "type": "integer", "description": "[{\"Name\":\"Active\",\"Value\":0},{\"Name\":\"InActive\",\"Value\":1}]", "format": "int32" } } ``` Generated enum: ```typescript enum Status { Active = 0, Inactive = 1, } ``` ## Notes - If the description doesn't match the expected format, the generator falls back to using enum values directly --- # `generateServices` **Type:** `boolean | undefined` | **Default:** `true` When set to `false`, the generator skips generating Angular services, producing only TypeScript types and interfaces. ## Usage ```typescript // openapi.config.ts import { GeneratorConfig } from 'ng-openapi'; const config: GeneratorConfig = { options: { generateServices: false }, ... // other configurations }; export default config; ``` After generation, you'll have: ``` src/client/ ├── models/ │ └── index.ts # TypeScript interfaces └── index.ts # Main exports ``` ## Notes - If services are not generated, the related utilities and providers will also not be generated --- # `modelFileStructure` **Type:** `'single' | 'per-type' | undefined` | **Default:** `'single'` Controls how the generated model declarations are laid out under `models/`. The default (`'single'`) keeps every interface, enum and type alias in one `models/index.ts`. With `'per-type'`, each schema gets its own file — easier to navigate for humans, and much friendlier to AI tooling that struggles with one huge file. ## Usage ```typescript // openapi.config.ts import { GeneratorConfig } from 'ng-openapi'; const config: GeneratorConfig = { options: { modelFileStructure: 'per-type' }, ... // other configurations }; export default config; ``` ## Example With `'per-type'`, a spec with `Order`, `OrderStatus` and `User` schemas produces: ``` models/ ├── index.ts # Barrel: export * from "./order"; … ├── order.ts # export interface Order { … } ├── order-status.ts # export type OrderStatus = … ├── user.ts # export interface User { … } └── request-options.ts # The RequestOptions SDK interface ``` File names are the kebab-cased raw schema names. Models referencing other models import them directly from the sibling file: ```typescript // models/order.ts import { OrderStatus } from "./order-status"; export interface Order { status: OrderStatus; ... } ``` `models/index.ts` becomes a pure barrel, so consuming code is unaffected either way — generated services keep importing from `../models`, and everything is still re-exported from the client's main `index.ts`. ## Notes - File names derive from the **undecorated** schema name: [`naming.models`](./naming) prefixes/suffixes decorate identifiers only, consistent with service file naming - Two schemas whose names kebab-case to the same file name (e.g. `UserProfile` and `user_profile`) are disambiguated with a numeric suffix (`user-profile-2.ts`) and reported as a warning. The fixed file names `index`, `request-options` and `request-params` are reserved, so schemas named like them (e.g. `Index`) get a suffixed file too - Schemas whose (decorated) **type names** collide — with each other, or with the built-in `RequestOptions` — produce TypeScript code that does not compile, in either file structure. Rename the schema or use [`naming.models`](./naming) to move the generated identifiers out of the way - With [`useSingleRequestParameter`](./use-single-request-parameter), `models/request-params.ts` is generated and re-exported through the barrel exactly as in single-file mode --- # `naming` **Type:** `NamingOptions | undefined` | **Default:** `undefined` (current names) Decorates the identifiers of generated classes and types with a prefix and/or suffix, so generated API classes can't collide with your own (e.g. your hand-written `RoleService` vs the generated `ApiRoleService`). Services, `httpResource` classes, and models are configured independently: ```typescript interface NamingOptions { services?: { prefix?: string; suffix?: string }; resources?: { prefix?: string; suffix?: string }; models?: { prefix?: string; suffix?: string }; } ``` - A **prefix** is prepended verbatim and must start a valid identifier (letters, digits, `_`). - For **services and resources**, a suffix *replaces* the default `Service`/`Resource` suffix — `suffix: 'ApiService'` yields `RoleApiService`, and an empty string `''` drops the suffix entirely. - For **models**, the suffix is plainly appended (models have no default suffix). - **File names are unaffected** — `role.service.ts` keeps its name; only the exported identifiers change. Import through the generated barrels as usual. ## Usage ```typescript // openapi.config.ts import { GeneratorConfig } from 'ng-openapi'; const config: GeneratorConfig = { options: { naming: { services: { prefix: 'Api' }, models: { suffix: 'Dto' } } }, ... // other configurations }; export default config; ``` ## Example ```typescript // default export class RoleService { ... } export interface User { ... } roleService.getUser(): Observable // naming: { services: { prefix: 'Api' }, models: { suffix: 'Dto' } } export class ApiRoleService { ... } export interface UserDto { ... } apiRoleService.getUser(): Observable ``` ## Notes - Model decoration applies to **schema-derived types only** (interfaces, enums and aliases generated from spec schemas). Operation-derived names are untouched: request-params interfaces (`GetPetByIdParams`), zod schemas, and SDK types like `RequestOptions` keep their names - Method parameter names derived from a body type follow the decorated name (e.g. a `User` body parameter named `user` becomes `userDto` with `models: { suffix: 'Dto' }`) - The `resources` group only takes effect with the [HTTP Resource plugin](../plugins/http-resource) - Prefixes/suffixes are validated as identifier fragments; anything else fails config validation --- # `responseTypeMapping` **Type:** `object | undefined` | **Default:** `undefined` Maps specific MIME types to Angular's `HttpResponseType` to customize how different response types are handled in generated services. ## Usage ```typescript // openapi.config.ts import { GeneratorConfig } from 'ng-openapi'; const config: GeneratorConfig = { options: { responseTypeMapping: { 'application/pdf': 'blob', 'application/json': 'json', 'text/plain': 'text', // Add more mappings as needed }, }, ... // other configurations }; export default config; ``` ## Schema ```typescript type ResponseTypeMapping = { [contentType: string]: "json" | "blob" | "arraybuffer" | "text"; }; ``` --- # `serviceDecorator` **Type:** `'injectable' | 'service' | undefined` | **Default:** `'injectable'` Selects the class decorator emitted on generated services (and, when the [HTTP Resource plugin](../plugins/http-resource) is enabled, on generated resource classes). With `'service'`, the generator emits Angular 22+'s [`@Service()`](https://angular.dev/guide/di/creating-and-using-services) decorator — an ergonomic shorthand for exactly `@Injectable({ providedIn: 'root' })` — and imports `Service` instead of `Injectable` from `@angular/core`. ::: warning Requires Angular 22+ `@Service()` does not exist below Angular 22, so code generated with `serviceDecorator: 'service'` will not compile on Angular ≤ 21. At the time of writing, `@Service` is also a **pre-release** API in Angular 22 and its shape may still change. The default (`'injectable'`) keeps today's output unchanged. ::: When `'service'` is set and an `@angular/core` older than 22 is detected in the workspace the generator runs in, a warning is printed; generation still proceeds, since the workspace running the CLI is not always the workspace that compiles the output (monorepos, CI). ## Usage ```typescript // openapi.config.ts import { GeneratorConfig } from 'ng-openapi'; const config: GeneratorConfig = { options: { serviceDecorator: 'service' }, ... // other configurations }; export default config; ``` ## Example ```typescript // serviceDecorator: 'injectable' (default) import { inject, Injectable } from "@angular/core"; @Injectable({ providedIn: "root" }) export class PetsService { ... } ``` ```typescript // serviceDecorator: 'service' import { inject, Service } from "@angular/core"; @Service() export class PetsService { ... } ``` ## Notes - `@Service()` maps only to `@Injectable({ providedIn: 'root' })` — the advanced `@Injectable` options (`useClass`, `useValue`, `useExisting`, `useFactory`) are not expressible with it. Generated services only ever use `providedIn: 'root'`, so this is not a limitation here - The generated utility interceptors (`DateInterceptor`, the base interceptor) keep their bare `@Injectable()` decorator: they are provided manually through tokens, not as root singletons, so `@Service()` does not apply to them - The option affects both `ng-openapi` services and `@ng-openapi/http-resource` resource classes consistently --- # `useSingleRequestParameter` **Type:** `boolean | undefined` | **Default:** `false` Generates service methods that take a single request object instead of one positional parameter per path/query/body parameter. Each operation gets a named, exported interface (e.g. `GetPetByIdParams`) containing all of its parameters, exported from `models/request-params.ts`. This makes call sites order-independent: adding a new parameter in the middle of an operation no longer silently shifts positional arguments. ## Usage ```typescript // openapi.config.ts import { GeneratorConfig } from 'ng-openapi'; const config: GeneratorConfig = { options: { useSingleRequestParameter: true }, ... // other configurations }; export default config; ``` ## Example Given an operation `GET /pet/{petId}` with a required `petId` path parameter and an optional `verbose` query parameter, the generated method changes from: ```typescript // useSingleRequestParameter: false (default) petService.getPetById(1, true).subscribe(); ``` to: ```typescript // useSingleRequestParameter: true import { GetPetByIdParams } from './api/models'; const request: GetPetByIdParams = { petId: 1, verbose: true }; petService.getPetById(request).subscribe(); ``` The request parameter contains **all** operation parameters: path parameters, query parameters, the JSON request body, and multipart/url-encoded form fields. The trailing `observe` and `options` parameters are unaffected. ## Notes - Operations without any parameters keep their signature unchanged and get no interface - If all parameters of an operation are optional, the request object itself is optional - Interface names are derived from the (possibly customized) method name: `getPetById` → `GetPetByIdParams`. If two services share a method name, the second interface is prefixed with the service name (e.g. `AdminGetPetByIdParams`) - All interfaces are re-exported through the `models` barrel - Operation parameters named `observe` or `options` conflict with the reserved trailing method parameters and cause a generation error when this option is enabled --- # `validation` **Type:** `{ response?: boolean } | undefined` | **Default:** `undefined` When setting the `response` property to `true`, it enables runtime validation of API responses against the OpenAPI schema using a validation library of your choice (e.g., `zod`, `valibot`, `ajv`, etc.). This is particularly useful for ensuring that the data received from the server conforms to the expected structure, enhancing type safety and reducing runtime errors. ## Usage ```typescript // openapi.config.ts import { GeneratorConfig } from 'ng-openapi'; const config: GeneratorConfig = { options: { validation: { response: true } }, ... // other configurations }; export default config; ``` After generation, you can use your preferred validation library in the `parse` option of the generated service methods to validate responses. ## Resources - [HttpResource Parsing and Validation ↗️](https://angular.dev/guide/http/http-resource#response-parsing-and-validation) --- # `output` **Type:** `string` | **Required** Output directory for generated files. ## Usage ```typescript // openapi.config.ts import { GeneratorConfig } from 'ng-openapi'; const config: GeneratorConfig = { input: './swagger.json', output: './src/client', ... // other configurations }; export default config; ``` After generation, you'll have: ``` src/client/ ├── models/ # TypeScript interfaces, enums ├── services/ # One Angular service per controller ├── tokens/ # Injection tokens ├── utils/ # Date transformer, download helpers, … ├── providers.ts # Provider setup function └── index.ts # Main exports ``` ## Notes - The directory is created if it doesn't exist; generated files are overwritten on every run - See [Generated Output](../../guide/generated-code.md) for a file-by-file tour --- # `plugins` **Type:** `IPluginGeneratorClass[]` | **Default: `undefined`** ## Usage ```typescript // openapi.config.ts import { GeneratorConfig } from 'ng-openapi'; import { HttpResourcePlugin } from '@ng-openapi/http-resource'; export default { plugins: [HttpResourcePlugin], ... // other configurations } as GeneratorConfig; ``` ::: warning `plugins` is a **top-level** configuration property, not part of `options`. A `plugins` array placed inside `options` is silently ignored. ::: ## Available Plugins - [HttpResourcePlugin](./plugins/http-resource.md) - [ZodPlugin](./plugins/zod.md) (Beta) ## Notes - Third-party plugins can be written against the documented contract — see [Plugin Authoring](../../guide/plugin-authoring.md) --- # `HttpResourcePlugin` The HTTP Resource plugin generates Angular services using the `httpResource` API for automatic caching, state management, and reactive data loading. [Learn more in Angular Docs ↗️](https://angular.dev/guide/http/http-resource) ## Usage ```typescript // openapi.config.ts import { GeneratorConfig } from 'ng-openapi'; import { HttpResourcePlugin } from '@ng-openapi/http-resource'; export default { plugins: [HttpResourcePlugin], // top-level, not inside `options` ... // other configurations } as GeneratorConfig; ``` ## Notes - Scoped interceptors are applied for resources as well - Currently only supports `GET` methods, as suggested by [Angular's documentation ↗️](https://angular.dev/guide/http/http-resource) - Generated resource classes honor [`serviceDecorator`](../options/service-decorator.md), so services and resources always use the same decorator - Generated resource class names default to `Resource` and can be decorated via [`naming.resources`](../options/naming.md); model references follow `naming.models` --- # `ZodPlugin` The `ZodPlugin` generates Zod schemas for your OpenAPI models, allowing for runtime validation of data structures in your Angular applications. :::warning Beta Feature This plugin is still in beta and may contain bugs. Please [report any issues](https://github.com/ng-openapi/ng-openapi/issues) you encounter. ::: ## Usage ```typescript // openapi.config.ts import { GeneratorConfig } from 'ng-openapi'; import { ZodPlugin } from '@ng-openapi/zod'; export default { plugins: [ZodPlugin], // top-level, not inside `options` ... // other configurations } as GeneratorConfig; ``` ## Notes - Zod v3 is not supported. --- # `validateInput` **Type:** `Function | undefined` | **Default:** `undefined` **Signature:** `(spec: SwaggerSpec) => boolean` Custom validation function to conditionally allow client generation based on the OpenAPI/Swagger specification. This is particularly useful when using URLs as input to avoid generation mistakes or ensure specification quality. ## Usage ```typescript // openapi.config.ts import { GeneratorConfig } from 'ng-openapi'; const config: GeneratorConfig = { input: 'https://api.example.com/swagger.json', validateInput: (spec) => { // Example validation: check if the title matches a specific value return spec.info.title === "Swagger Petstore - OpenAPI 3.0"; }, ... // other configurations }; export default config; ``` ## Parameters - **spec**: The parsed OpenAPI/Swagger specification object ## Return Value - **boolean**: `true` to proceed with generation, `false` to stop generation ## Notes - Useful for remote URLs where specification content may vary, especially in development or local environments --- # Providers Every generated client ships a provider function in its `providers.ts` that sets up the client in your Angular application. The function is named after the [`clientName`](configuration/client-name.md): `provideClient` (e.g. `providePetStoreClient`). Without a `clientName` it is `provideDefaultClient`. ## Usage ```typescript import { provideDefaultClient } from "./api/providers"; // generated file in your output directory import { provideHttpClient } from "@angular/common/http"; import { ApplicationConfig } from "@angular/core"; export const appConfig: ApplicationConfig = { providers: [ provideHttpClient(), provideDefaultClient({ basePath: "https://api.example.com", }), ], }; ``` ::: info `provideNgOpenapi` still exists as a deprecated alias of `provideDefaultClient` for clients generated without a `clientName`. ::: ## Configuration Options ### `basePath` **Type:** `string` | **Required** The base URL for your API. This is prepended to all API requests. ### `interceptors` **Type:** `(new () => HttpInterceptor)[]` | **Default:** `[]` Apply client specific interceptors. Pass the interceptor **classes** (not instances) — the provider instantiates them for you. This is not going to replace the global interceptors configured in your application, but will be applied to requests made by the provided client. Interceptors can be re-used across different clients. ```typescript provideDefaultClient({ basePath: "https://api.example.com", interceptors: [AuthInterceptor, LoggingInterceptor], // classes, not instances }); ``` ### `enableDateTransform` **Type:** `boolean` | **Default:** `true` If disabled, [Date Transformer Interceptor](utilities/date-transformer.md) will not be applied to the HTTP client. This means date strings will not be automatically converted to `Date` objects. ### `dateTransformRegex` **Type:** `RegExp` | **Default:** [`ISO_DATE_REGEX`](utilities/date-transformer.md#recognized-formats) Overrides the pattern the [Date Transformer Interceptor](utilities/date-transformer.md) uses to detect which string values are dates. Use it when your API returns a format the default pattern doesn't cover, without having to copy the generated `date-transformer.ts`. Only available when the client was generated with `dateType: 'Date'`. ```typescript provideDefaultClient({ basePath: "https://api.example.com", dateTransformRegex: /your-custom-pattern/, }); ``` ## Manual Configuration If you prefer to manually configure the OpenAPI client without using the provider, you can do so by setting up the base path token from the generated `tokens/` directory. This is useful for more complex scenarios where you need fine-grained control over the configuration. ```typescript import { provideHttpClient } from "@angular/common/http"; import { ApplicationConfig } from "@angular/core"; import { BASE_PATH } from "./api/tokens"; export const appConfig: ApplicationConfig = { providers: [provideHttpClient(), { provide: BASE_PATH, useValue: "https://api.example.com" }], }; ``` The token is named after the client (`BASE_PATH_`, e.g. `BASE_PATH_PETSTORE`); for the default client, `BASE_PATH` is a deprecated alias of `BASE_PATH_DEFAULT`. --- # Utilities A collection of Angular utilities to enhance your development experience with common patterns and functionality. ## Available Utilities ### [Date Transformer](utilities/date-transformer.md) An Angular [HTTP Interceptor ↗️](https://angular.dev/guide/http/interceptors) that automatically converts date strings from API responses into JavaScript `Date` objects. ```typescript // Automatically transforms ISO date strings to Date objects { "createdAt": "2024-01-15T10:30:00Z" } // becomes { "createdAt": Date } ``` ### [File Download Helper](utilities/file-download-helper.md) A simple [RxJS operator ↗️](https://rxjs.dev/guide/operators) for handling file downloads in Angular applications. ```typescript this.reportService.getReportById(123).pipe(downloadFileOperator("report.pdf")).subscribe(); ``` ## Usage These utilities are designed to work seamlessly with Angular applications and follow Angular best practices. Each utility can be used independently or together as part of your application's architecture. --- # Date Transformer An Angular [HTTP Interceptor ↗️](https://angular.dev/guide/http/interceptors) that automatically converts date strings from API responses into JavaScript `Date` objects. ## Usage The Date Transformer is generated by default when you set the `dateType` option to `'Date'` in your [OpenAPI configuration](../configuration/options/date-type.md). The interceptor will be applied automatically to your HTTP client, if you are using the [generated provider function](../providers.md) (`provideDefaultClient` / `provideClient`) and the [`enableDateTransform` option isn't disabled](../providers.md#enabledatetransform). ### Manual Setup If you chose to configure the OpenAPI client [manually](../providers.md#manual-configuration) or want to add the Date Transformer interceptor separately, register the class-based `DateInterceptor` through the `HTTP_INTERCEPTORS` multi-provider in your `app.config.ts` (`withInterceptors` only accepts functional interceptors): ```typescript import { ApplicationConfig } from "@angular/core"; import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi } from "@angular/common/http"; import { DateInterceptor } from "./client/utils/date-transformer"; import { BASE_PATH } from "./client/tokens"; export const appConfig: ApplicationConfig = { providers: [ provideHttpClient(withInterceptorsFromDi()), { provide: HTTP_INTERCEPTORS, useClass: DateInterceptor, multi: true }, { provide: BASE_PATH, useValue: "https://api.example.com" }, ], }; ``` ## Generated Source ```typescript // client/utils/date-transformer.ts import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from "@angular/common/http"; import { Injectable } from "@angular/core"; import { Observable } from "rxjs"; import { map } from "rxjs/operators"; export const ISO_DATE_REGEX = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:?\d{2})?$/; export function transformDates(obj: any, dateRegex: RegExp = ISO_DATE_REGEX): any { if (obj === null || obj === undefined || typeof obj !== "object") { return obj; } if (obj instanceof Date) { return obj; } if (Array.isArray(obj)) { return obj.map((item) => transformDates(item, dateRegex)); } if (typeof obj === "object") { const transformed: any = {}; for (const key of Object.keys(obj)) { const value = obj[key]; if (typeof value === "string" && dateRegex.test(value)) { transformed[key] = new Date(value); } else { transformed[key] = transformDates(value, dateRegex); } } return transformed; } return obj; } @Injectable() export class DateInterceptor implements HttpInterceptor { /** @param dateRegex Optional override for the pattern used to detect ISO date strings. */ constructor(private readonly dateRegex: RegExp = ISO_DATE_REGEX) {} intercept(req: HttpRequest, next: HttpHandler): Observable> { return next.handle(req).pipe( map((event) => { if (event instanceof HttpResponse && event.body) { return event.clone({ body: transformDates(event.body, this.dateRegex) }); } return event; }), ); } } ``` ## Recognized Formats `ISO_DATE_REGEX` matches a full RFC 3339 / ISO 8601 date-time with optional fractional seconds (of any length) and an optional `Z` or numeric timezone offset (`±hh:mm` or `±hhmm`): - `2024-01-15T10:30:00Z` - `2024-01-15T10:30:00.123Z` - `2024-01-15T10:30:00.04` — any number of fractional-second digits - `2024-01-15T10:30:00.7559265+02:00` — numeric timezone offset - `2024-01-15T10:30:00+0200` — offset without colon - `2024-01-15T10:30:00` The pattern is intentionally strict (full date-time only) so plain strings such as a bare year (`"2024"`) or a numeric ID are never accidentally turned into `Date` objects. ## Customizing the Regex If your API uses a format the default pattern doesn't cover, override it via the provider instead of editing the generated file: ```typescript provideDefaultClient({ basePath: "https://api.example.com", dateTransformRegex: /your-custom-pattern/, }); ``` Both `transformDates` and `DateInterceptor` accept the regex directly as well, for manual interceptor setups: ```typescript new DateInterceptor(/your-custom-pattern/); transformDates(responseBody, /your-custom-pattern/); ``` --- # File Download Helper A simple [RxJS operator ↗️](https://rxjs.dev/guide/operators) for handling file downloads in Angular applications. ## Usage Import and use the `downloadFileOperator` with any Observable that emits Blob data: ```typescript import { Component, inject } from "@angular/core"; import { downloadFileOperator } from "./api/utils/file-download"; export class ReportComponent { private readonly reportService = inject(ReportService); downloadReport() { this.reportService.getReportById(123).pipe(downloadFileOperator("report.pdf")).subscribe(); } } ``` ## Generated Source ```typescript // client/utils/file-download.ts import { Observable } from "rxjs"; import { tap } from "rxjs/operators"; export function downloadFile(blob: Blob, filename: string, mimeType?: string): void { // Create a temporary URL for the blob const url = window.URL.createObjectURL(blob); // Create a temporary anchor element and trigger download const link = document.createElement("a"); link.href = url; link.download = filename; // Append to body, click, and remove document.body.appendChild(link); link.click(); document.body.removeChild(link); // Clean up the URL window.URL.revokeObjectURL(url); } export function downloadFileOperator( filename: string | ((blob: T) => string), mimeType?: string, ): (source: Observable) => Observable { return (source: Observable) => { return source.pipe( tap((blob: T) => { const actualFilename = typeof filename === "function" ? filename(blob) : filename; downloadFile(blob, actualFilename, mimeType); }), ); }; } export function extractFilenameFromContentDisposition( contentDisposition: string | null, fallbackFilename: string = "download", ): string { if (!contentDisposition) { return fallbackFilename; } // Try to extract filename from Content-Disposition header // Supports both "filename=" and "filename*=" formats const filenameMatch = contentDisposition.match(/filename\*?=['"]?([^'"\n;]+)['"]?/i); if (filenameMatch && filenameMatch[1]) { // Decode if it's RFC 5987 encoded (filename*=UTF-8''...) const filename = filenameMatch[1]; if (filename.includes("''")) { const parts = filename.split("''"); const encoded = parts.length === 2 ? parts[1] : undefined; if (encoded) { try { return decodeURIComponent(encoded); } catch { return encoded; } } } return filename; } return fallbackFilename; } ``` --- # `@ng-openapi/http-resource` NPM: ## [0.1.2](https://github.com/ng-openapi/ng-openapi/compare/http-resource-v0.1.1...http-resource-v0.1.2) (2026-07-27) ### Bug Fixes * **generator:** generate correct ts-nocheck comment ([#116](https://github.com/ng-openapi/ng-openapi/issues/116)) ([dcf8590](https://github.com/ng-openapi/ng-openapi/commit/dcf8590d9900d0a0e85688e2866415508fe73035)) * **generator:** keep `| null` on nullable strings with uuid/email/uri/binary formats ([#119](https://github.com/ng-openapi/ng-openapi/issues/119)) ([6a3c307](https://github.com/ng-openapi/ng-openapi/commit/6a3c307359b740ba919a7df191a83a76b2a6413c)) ## [0.1.1](https://github.com/ng-openapi/ng-openapi/compare/http-resource-v0.1.0...http-resource-v0.1.1) (2026-07-06) ### Features * add modelFileStructure option for one model file per schema ([#113](https://github.com/ng-openapi/ng-openapi/issues/113)) ([085cc53](https://github.com/ng-openapi/ng-openapi/commit/085cc5339acf7edc8f052488e47581227d19a57b)), closes [#62](https://github.com/ng-openapi/ng-openapi/issues/62) * add naming customization for generated services, resources and models ([#111](https://github.com/ng-openapi/ng-openapi/issues/111)) ([0ac5ffe](https://github.com/ng-openapi/ng-openapi/commit/0ac5ffe6ba50b1dc76ae07ca50a593957ef2d223)), closes [#16](https://github.com/ng-openapi/ng-openapi/issues/16) * send Accept header derived from response content types ([#112](https://github.com/ng-openapi/ng-openapi/issues/112)) ([6d3a932](https://github.com/ng-openapi/ng-openapi/commit/6d3a932da5b0f113dcd272937cdecfd00224f42e)) * support emitting Angular 22 @Service decorator for generated services ([#110](https://github.com/ng-openapi/ng-openapi/issues/110)) ([54bb1f4](https://github.com/ng-openapi/ng-openapi/commit/54bb1f4da6365c823d59573daae4b4b20d5361d2)), closes [#104](https://github.com/ng-openapi/ng-openapi/issues/104) ### Bug Fixes * **ng-openapi:** tolerate path-less specs — derive index exports from the Project ([#107](https://github.com/ng-openapi/ng-openapi/issues/107)) ([58c01e6](https://github.com/ng-openapi/ng-openapi/commit/58c01e67149f6918df27445cdbe0cff02a0f7752)) ## [0.1.0](https://github.com/ng-openapi/ng-openapi/compare/http-resource-v0.0.32...http-resource-v0.1.0) (2026-07-06) ### ⚠ BREAKING CHANGES * maintainability refactor — normalized spec model, generator decomposition, OpenAPI 3.1, docs & AI-readiness (phases 0–6) ([#103](https://github.com/ng-openapi/ng-openapi/issues/103)) ### Features * maintainability refactor — normalized spec model, generator decomposition, OpenAPI 3.1, docs & AI-readiness (phases 0–6) ([#103](https://github.com/ng-openapi/ng-openapi/issues/103)) ([963ffb4](https://github.com/ng-openapi/ng-openapi/commit/963ffb4f80fac3814be09d47a706882aa3a118c1)) ## [0.0.32](https://github.com/ng-openapi/ng-openapi/compare/http-resource-v0.0.31...http-resource-v0.0.32) (2026-06-10) ### Features * **deps:** refresh dev tooling, migrate Nx 22 and bump packages to fix vulnerabilities ([#82](https://github.com/ng-openapi/ng-openapi/issues/82)) ([5355aab](https://github.com/ng-openapi/ng-openapi/commit/5355aabfc67b4579413b7f5b742417d32b4af734)) ## 0.0.31 (2026-05-16) _No user-facing changes since 0.0.30 — version bumped for consistency across the workspace._ --- # Changelog Release notes for each published package in the ng-openapi workspace. - [**ng-openapi**](./ng-openapi) — core generator (types + Angular `HttpClient` services) - [**@ng-openapi/http-resource**](./http-resource) — plugin that emits Angular `httpResource()` calls - [**@ng-openapi/zod**](./zod) — plugin that emits Zod runtime validators Each package follows [Conventional Commits](https://www.conventionalcommits.org/) and is released automatically by [release-please](https://github.com/googleapis/release-please). Versions stay on `0.x` until the first major release. Under that policy: - Breaking changes bump the **minor** version (e.g. `0.2.18 → 0.3.0`). - New features and bug fixes bump the **patch** version (e.g. `0.2.18 → 0.2.19`). Tagged releases on GitHub: . --- # `ng-openapi` NPM: ## [0.3.2](https://github.com/ng-openapi/ng-openapi/compare/ng-openapi-v0.3.1...ng-openapi-v0.3.2) (2026-07-27) ### Bug Fixes * **generator:** generate correct ts-nocheck comment ([#116](https://github.com/ng-openapi/ng-openapi/issues/116)) ([dcf8590](https://github.com/ng-openapi/ng-openapi/commit/dcf8590d9900d0a0e85688e2866415508fe73035)) * **generator:** keep `| null` on nullable strings with uuid/email/uri/binary formats ([#119](https://github.com/ng-openapi/ng-openapi/issues/119)) ([6a3c307](https://github.com/ng-openapi/ng-openapi/commit/6a3c307359b740ba919a7df191a83a76b2a6413c)) ## [0.3.1](https://github.com/ng-openapi/ng-openapi/compare/ng-openapi-v0.3.0...ng-openapi-v0.3.1) (2026-07-06) ### Features * add modelFileStructure option for one model file per schema ([#113](https://github.com/ng-openapi/ng-openapi/issues/113)) ([085cc53](https://github.com/ng-openapi/ng-openapi/commit/085cc5339acf7edc8f052488e47581227d19a57b)), closes [#62](https://github.com/ng-openapi/ng-openapi/issues/62) * add naming customization for generated services, resources and models ([#111](https://github.com/ng-openapi/ng-openapi/issues/111)) ([0ac5ffe](https://github.com/ng-openapi/ng-openapi/commit/0ac5ffe6ba50b1dc76ae07ca50a593957ef2d223)), closes [#16](https://github.com/ng-openapi/ng-openapi/issues/16) * send Accept header derived from response content types ([#112](https://github.com/ng-openapi/ng-openapi/issues/112)) ([6d3a932](https://github.com/ng-openapi/ng-openapi/commit/6d3a932da5b0f113dcd272937cdecfd00224f42e)) * support emitting Angular 22 @Service decorator for generated services ([#110](https://github.com/ng-openapi/ng-openapi/issues/110)) ([54bb1f4](https://github.com/ng-openapi/ng-openapi/commit/54bb1f4da6365c823d59573daae4b4b20d5361d2)), closes [#104](https://github.com/ng-openapi/ng-openapi/issues/104) ### Bug Fixes * **ng-openapi:** tolerate path-less specs — derive index exports from the Project ([#107](https://github.com/ng-openapi/ng-openapi/issues/107)) ([58c01e6](https://github.com/ng-openapi/ng-openapi/commit/58c01e67149f6918df27445cdbe0cff02a0f7752)) ## [0.3.0](https://github.com/ng-openapi/ng-openapi/compare/ng-openapi-v0.2.22...ng-openapi-v0.3.0) (2026-07-06) ### ⚠ BREAKING CHANGES * maintainability refactor — normalized spec model, generator decomposition, OpenAPI 3.1, docs & AI-readiness (phases 0–6) ([#103](https://github.com/ng-openapi/ng-openapi/issues/103)) ### Features * maintainability refactor — normalized spec model, generator decomposition, OpenAPI 3.1, docs & AI-readiness (phases 0–6) ([#103](https://github.com/ng-openapi/ng-openapi/issues/103)) ([963ffb4](https://github.com/ng-openapi/ng-openapi/commit/963ffb4f80fac3814be09d47a706882aa3a118c1)) ## [0.2.22](https://github.com/ng-openapi/ng-openapi/compare/ng-openapi-v0.2.21...ng-openapi-v0.2.22) (2026-07-05) ### Bug Fixes * **ng-openapi:** avoid fixMissingImports crash in request-params generation ([#94](https://github.com/ng-openapi/ng-openapi/issues/94)) ([8a30358](https://github.com/ng-openapi/ng-openapi/commit/8a303581158eb86e709e6b1489b3542e9a88da74)) ## [0.2.21](https://github.com/ng-openapi/ng-openapi/compare/ng-openapi-v0.2.20...ng-openapi-v0.2.21) (2026-07-05) ### Features * **ng-openapi:** add useSingleRequestParameter option for request object parameters ([#92](https://github.com/ng-openapi/ng-openapi/issues/92)) ([9085680](https://github.com/ng-openapi/ng-openapi/commit/908568009bb87caaa9dee316f1869ee549a09ff4)) ## [0.2.20](https://github.com/ng-openapi/ng-openapi/compare/ng-openapi-v0.2.19...ng-openapi-v0.2.20) (2026-06-10) ### Features * **date-transformer:** customizable date regex with broader default ([#87](https://github.com/ng-openapi/ng-openapi/issues/87)) ([2ff96d6](https://github.com/ng-openapi/ng-openapi/commit/2ff96d651984ee0e7f076553db0a30922e446a6b)) * **deps:** refresh dev tooling, migrate Nx 22 and bump packages to fix vulnerabilities ([#82](https://github.com/ng-openapi/ng-openapi/issues/82)) ([5355aab](https://github.com/ng-openapi/ng-openapi/commit/5355aabfc67b4579413b7f5b742417d32b4af734)) ## [0.2.19](https://github.com/ng-openapi/ng-openapi/compare/ng-openapi@0.2.18...ng-openapi-v0.2.19) (2026-05-16) ### Bug Fixes - **file-download.generator:** error when using strict typings ([40396ad](https://github.com/ng-openapi/ng-openapi/commit/40396ad042735a3af45fc288c2e82a91fc7951a8)) - **file-download.generator:** error when using strict typings ([6c00a59](https://github.com/ng-openapi/ng-openapi/commit/6c00a59fc9ec273bdca764f7436f6fecfae26451)) --- # `@ng-openapi/zod` NPM: ## [0.1.2](https://github.com/ng-openapi/ng-openapi/compare/zod-v0.1.1...zod-v0.1.2) (2026-07-27) ### Bug Fixes * **generator:** generate correct ts-nocheck comment ([#116](https://github.com/ng-openapi/ng-openapi/issues/116)) ([dcf8590](https://github.com/ng-openapi/ng-openapi/commit/dcf8590d9900d0a0e85688e2866415508fe73035)) * **generator:** keep `| null` on nullable strings with uuid/email/uri/binary formats ([#119](https://github.com/ng-openapi/ng-openapi/issues/119)) ([6a3c307](https://github.com/ng-openapi/ng-openapi/commit/6a3c307359b740ba919a7df191a83a76b2a6413c)) ## [0.1.1](https://github.com/ng-openapi/ng-openapi/compare/zod-v0.1.0...zod-v0.1.1) (2026-07-06) ### Features * add modelFileStructure option for one model file per schema ([#113](https://github.com/ng-openapi/ng-openapi/issues/113)) ([085cc53](https://github.com/ng-openapi/ng-openapi/commit/085cc5339acf7edc8f052488e47581227d19a57b)), closes [#62](https://github.com/ng-openapi/ng-openapi/issues/62) * send Accept header derived from response content types ([#112](https://github.com/ng-openapi/ng-openapi/issues/112)) ([6d3a932](https://github.com/ng-openapi/ng-openapi/commit/6d3a932da5b0f113dcd272937cdecfd00224f42e)) ### Bug Fixes * **ng-openapi:** tolerate path-less specs — derive index exports from the Project ([#107](https://github.com/ng-openapi/ng-openapi/issues/107)) ([58c01e6](https://github.com/ng-openapi/ng-openapi/commit/58c01e67149f6918df27445cdbe0cff02a0f7752)) ## [0.1.0](https://github.com/ng-openapi/ng-openapi/compare/zod-v0.0.10...zod-v0.1.0) (2026-07-06) ### ⚠ BREAKING CHANGES * maintainability refactor — normalized spec model, generator decomposition, OpenAPI 3.1, docs & AI-readiness (phases 0–6) ([#103](https://github.com/ng-openapi/ng-openapi/issues/103)) ### Features * maintainability refactor — normalized spec model, generator decomposition, OpenAPI 3.1, docs & AI-readiness (phases 0–6) ([#103](https://github.com/ng-openapi/ng-openapi/issues/103)) ([963ffb4](https://github.com/ng-openapi/ng-openapi/commit/963ffb4f80fac3814be09d47a706882aa3a118c1)) ## [0.0.10](https://github.com/ng-openapi/ng-openapi/compare/zod-v0.0.9...zod-v0.0.10) (2026-06-10) ### Features * **deps:** refresh dev tooling, migrate Nx 22 and bump packages to fix vulnerabilities ([#82](https://github.com/ng-openapi/ng-openapi/issues/82)) ([5355aab](https://github.com/ng-openapi/ng-openapi/commit/5355aabfc67b4579413b7f5b742417d32b4af734)) ## 0.0.9 (2026-05-16) _No user-facing changes since 0.0.8 — version bumped for consistency across the workspace._ --- # About ng-openapi is an open-source Angular-first OpenAPI client generator, released under the [MIT license](https://github.com/ng-openapi/ng-openapi/blob/main/LICENSE). It is created and maintained by [Tareq Jami](https://tareqjami.de), a software engineer based in Hamburg, Germany, who also runs the freelance practice [Jami IT](https://jami-it.de). If you'd like to support the project, you can [sponsor it on GitHub](https://github.com/sponsors/ng-openapi). The library stays free and MIT-licensed regardless. For commercial support around it — OpenAPI and Angular integration work, migrations off hand-written clients, or architecture review — [Jami IT](https://jami-it.de) is where that happens.