Releases: ls1intum/openapi-generator-angular22
Release list
1.0.0 — First release
openapi-generator-angular22 1.0.0 — first release 🎉
This is the inaugural release of openapi-generator-angular22, and its first publication to Maven Central:
de.tum.cit.aet:openapi-generator-angular22:1.0.0
Because there is no previous version, these notes describe what the generator does rather than what changed — the whole thing is new.
What it is
A custom generator that plugs into the standard OpenAPI Generator toolchain (CLI, Maven, and Gradle) under the generator name angular22. Point it at an OpenAPI spec and it emits an idiomatic, strictly-typed Angular 22 + TypeScript HTTP client that follows current Angular best practices — signals, standalone services, and the inject() function — instead of the NgModule/constructor-injection style the stock typescript-angular generator produces.
It resolves from a plain mavenCentral() with no authentication, so any Maven or Gradle project can consume it directly.
Why it exists
The stock generator emits services that rely on constructor parameter-property injection. That pattern miscompiles under some modern Angular build paths (e.g. AnalogJS fastCompile used by Vitest), surfacing as an NG0202 runtime error, and it predates Angular's signal APIs entirely. This generator was built to produce a client that is native to the Angular 22 era and safe across those build paths.
Features
Two GET strategies — pick per project
-
Signal-based
httpResource(default). GET operations are emitted as functions returning anHttpResourceRef<T>. They are reactive and auto-refetch when their input signals change, and exposeisLoading(),hasValue(),value(), andreload()— a natural fit for signal-driven components.export function getCourseResource(courseId: Signal<number> | number): HttpResourceRef<Course | undefined> { return httpResource<Course>(() => `${BASE_PATH}/courses/${typeof courseId === 'function' ? courseId() : courseId}`); }
-
Classical
HttpClientObservables (useHttpResource=false). GET operations are emitted asHttpClientmethods returningObservable<T>, alongside the mutations. This is the low-friction option for codebases built around.subscribe()/ RxJS pipelines, or migrating incrementally. Both modes are first-class and fully supported.
inject()-based, standalone services
Mutations (POST / PUT / PATCH / DELETE) are grouped per tag into an @Injectable({ providedIn: 'root' }) service that uses the inject() function rather than constructor injection. Services are standalone — no NgModules required — and are tree-shakable via providedIn: 'root'.
@Injectable({ providedIn: 'root' })
export class CourseApi {
private readonly http = inject(HttpClient);
private readonly basePath = '/api';
createCourse(courseCreate: CourseCreate): Observable<Course> { … }
updateCourse(courseId: number, courseUpdate: CourseUpdate): Observable<Course> { … }
deleteCourse(courseId: number): Observable<void> { … }
}(Set useInjectFunction=false if you prefer classic constructor(private http: HttpClient) services.)
Strict, intention-revealing models
- Response DTOs are emitted with
readonlyproperties, so client code can't accidentally mutate data it received from the server. - Input DTOs stay mutable — the generator recognises request payload shapes (names ending in
Create/Update/Request/Input) and leaves their fields writable, so you can build them up before sending. - Model-to-model references are imported correctly (relative
./paths), and array and optional (?) fields are typed faithfully to the spec.
Enums as string-literal unions and a runtime values object
Each enum is emitted both as a TypeScript string-literal union type (maximum type-safety at call sites) and as an accompanying runtime const object, so you get named values and a values array without a heavyweight TS enum:
export type CourseState = 'ACTIVE' | 'ARCHIVED' | 'DRAFT';
export const CourseState = { Active: 'ACTIVE', Archived: 'ARCHIVED', Draft: 'DRAFT' } as const;Arrays of enums are typed as Array<…> correctly.
Faithful HTTP semantics
The generated client mirrors what each endpoint actually does over the wire:
- No-content endpoints return
Observable<void>with an explicit<void>type parameter on the request. - File-download endpoints (binary / non-JSON) return
Observable<HttpResponse<Blob>>with the correctresponseType, so both the payload and response metadata are available. - Path parameters are interpolated correctly whether numeric or non-numeric (no stray
{param}placeholders, no double-encoding). - Array query parameters are serialised as repeated keys (
?x=a&x=b), which Spring and most backends accept out of the box.
Clean file organization
With separateResources=true, signal GET resources land in *-resources.ts and mutation services in *-api.ts — a clear split between reactive reads and imperative writes. Set it to false to co-locate everything in one *-api.ts per tag. ES6+ output (supportsES6=true) uses template literals and modern constructs.
Ecosystem compatibility
Built on the standard OpenAPI Generator framework, so it works everywhere the toolchain does — the CLI, the Maven plugin, and the Gradle plugin — using the generator name angular22.
Configuration options
| Option | Default | Description |
|---|---|---|
useHttpResource |
true |
GET operations use signal-based httpResource; set false for classical HttpClient Observables. |
useInjectFunction |
true |
Services use the inject() function; set false for constructor injection. |
separateResources |
true |
Emit GET resources into separate *-resources.ts files (only with useHttpResource=true). |
readonlyModels |
true |
Mark response-model properties readonly (input DTOs stay mutable). |
supportsES6 |
true |
Emit ES6+ code (template literals, modern constructs). |
Installation
Add the generator as a dependency of the OpenAPI Generator plugin and set generatorName to angular22. Full Gradle (Kotlin & Groovy DSL), Maven, and CLI snippets are in the README. Minimal Gradle example:
dependencies {
openapiGenerator("de.tum.cit.aet:openapi-generator-angular22:1.0.0")
}
openApiGenerate {
generatorName.set("angular22")
inputSpec.set("$projectDir/src/main/resources/openapi.yaml")
outputDir.set("$buildDir/generated/openapi")
configOptions.set(mapOf("useHttpResource" to "true", "useInjectFunction" to "true"))
}To build the generator from source into your local Maven repo (no signing key required), run ./gradlew publishToMavenLocal.
Compatibility
- Angular 22 / modern TypeScript, signal APIs, standalone components.
- Runs on the OpenAPI Generator 7.x toolchain (CLI / Maven / Gradle).
- License: MIT.