Skip to content

Releases: ls1intum/openapi-generator-angular22

1.0.0 — First release

Choose a tag to compare

@github-actions github-actions released this 11 Jul 21:04

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 an HttpResourceRef<T>. They are reactive and auto-refetch when their input signals change, and expose isLoading(), hasValue(), value(), and reload() — 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 HttpClient Observables (useHttpResource=false). GET operations are emitted as HttpClient methods returning Observable<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 readonly properties, 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 correct responseType, 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.