Zum Hauptinhalt springen
Entwicklung Mobil

OpenAPI TypeScript: Types, Clients und Validierung generieren

Erfahre, wie die OpenAPI-TypeScript-Generierung end to end funktioniert. Generiere Types, binde Clients ein, validiere zur Laufzeit und liefere sicher aus der CI aus.

OpenAPI TypeScript: Types, Clients und Validierung generieren

Man erkennt den Moment meistens daran, wenn eine API Pipeline anfängt, das Team anzulügen. Ein Schema ändert sich, die generierten Types aktualisieren sich ohne Murren, der PR wird grün, und dann liest jemand im Frontend weiterhin die alte Response-Struktur, weil der Wrapper den Fehler wegcastet. Das ist das OpenAPI-TypeScript-Problem – nicht die Frage, ob ein Generator Interfaces ausspucken kann.

Die nützliche Frage ist schwieriger. Welchen Contract willst du zwischen Schema, Transport und Validierung – und welche Teile sollten zur Build-Zeit schnell fehlschlagen, statt in die Laufzeit durchzusickern? Sobald du das OpenAPI TypeScript als Pipeline-Entscheidung betrachtest, werden die Trade-offs viel klarer und die Tools hören auf, so zu tun, als wären sie die Komplettlösung.

Inhaltsverzeichnis

Warum generierte Typen nicht dasselbe sind wie ein sicheres API

Ein Teammitglied merget einen PR, der ein optionales Response-Feld hinzufügt. Die generierte Datei aktualisiert sich sauber, der Diff sieht unauffällig aus und alle machen weiter. Doch das Frontend liest weiterhin ein altes Shape über einen manuellen Wrapper, der „temporär“ gecastet wurde mit as any, und in der Production verhält sich alles so, als hätte sich der Contract nie geändert.

Das ist die Falle bei generierten Types. TypeScript kann nur den code schützen, der die generierten Types konsumiert, und das auch nur, wenn der Transport Layer den Contract nicht wieder löscht. Die OpenAPI-Seite liefert dir ein Schema, aber keine Garantie, dass jeder Caller es einhält. Der Diskussionen über das Verständnis von API Verbindungen sind hier hilfreich, da sie den Fokus weg von einem einzelnen Tool hin zur Systemverbindung lenken.

Wo die Fehler lauern

Die häufigsten Fehlerquellen sind eher banal als exotisch. Schema Drift tritt auf, wenn die OpenAPI-Spec und der deployte Service nicht mehr übereinstimmen. Teilweise Abdeckung zeigt sich, wenn eine Spec nur den Happy Path modelliert, die App aber von undokumentierten Edge Cases abhängt. Manuell geschriebene Wrapper sind oft der Grund, warum Types geschwächt werden – besonders wenn jemand „schnell liefern“ will und any oder einen lockeren Response-Cast nutzt.

Praktische Regel: Wenn der Wrapper lügt, kann dich auch der Generator nicht retten.

Zudem gibt es eine Runtime-Lücke. TypeScript-Typen verschwinden nach der Kompilierung, sodass sie fehlerhaftes JSON über das Netzwerk nicht ablehnen können. Das Netzwerk kümmert sich nicht darum, was dein Editor inferiert hat – deshalb ist ein generierter Client nur eine Ebene in einer sichereren API Pipeline.

Die übergeordnete operative Frage ist Sicherheit und Contract-Disziplin, nicht nur Developer Convenience. Wenn du eine strukturierte Übersicht darüber suchst, wie API Contracts in einen größeren App-Lifecycle passen, ist dieser interne Guide zu API Security-Standards für App-Store-Compliance ein hilfreicher Begleiter.

Der professionelle Ansatz für openapi-typescript sieht so aus. Er bietet eine strikte Schema-zu-Typen-Bridge, was hervorragend ist, aber er validiert keine Requests, erzwingt nicht die Payload-Struktur zur Laufzeit und verhindert nicht, dass ein unsauberer Wrapper alles untergräbt. Der Generator sind die einfachen 20 Prozent. Der Rest ist Pipeline-Design – und genau dort gewinnen Teams entweder Vertrauen oder bauen falsche Sicherheit auf.

TypeScript-Typen aus einer OpenAPI-Spec generieren

Screenshot von https://openapi-ts.dev

Das leichteste, nützliche Setup ist meist dasjenige, das echtem Repo-Churn standhält. Behalte die OpenAPI-Spec im selben Repository, generiere eine committete Type-Datei und mache Drift in der CI sichtbar, anstatt darauf zu hoffen, dass jemand an den Refresh-Schritt denkt. Ein Befehl wie npx openapi-typescript ./openapi.yaml -o ./src/types/api.d.ts liefert dir eine deterministische Output-Datei, die Reviewer wie jede andere Code-Änderung prüfen können.

Die Flags, auf die es wirklich ankommt

Der -o das output-Flag ist wichtig, da es das generierte Artefakt explizit macht. --immutable ist nützlich, wenn die generierten Typen die readonly-Absicht im Output beibehalten sollen, und --alphabetize hält Diffs stabil, wenn sich die Schema-Reihenfolge ohne semantische Bedeutung ändert. --enum ist entscheidend, wenn dein Team Enums in der generierten Oberfläche statt Unions bevorzugt.

Die eigene Dokumentation des Projekts ist klar in Bezug auf den Scope: Es ist ein Type-Generator, kein Client-Runtime oder Request-Layer. Diese Einschränkung hilft bei einem leichtgewichtigen Type-first-Setup. Das Repository zeigt zudem das Wartungsmodell des Tools – ein Grund, warum Open-Source-Tools in der Produktion bestehen können, wenn Docs und Releases aktiv bleiben, wie in dem Fall für Open-Source-Wartungbesprochen. Eine praxisnahe Lektüre dazu bietet das GitHub Repository und die CLI Dokumentation.

Binde die Generierung so ein, package.json dass der Befehl direkt neben den restlichen Build-Scripts liegt, und führe ihn bei jeder Spec-Änderung aus. In der CI regeneriere die Datei und schlage fehl, wenn git diff Drift auftritt. So werden Contract-Änderungen zu sichtbarer Review-Arbeit statt zu einem stillen Runtime-Risiko.

Die Schema-Seite ist genauso wichtig wie die Command Line. Das Projekt empfiehlt compilerOptions.noUncheckedIndexedAccess damit additionalProperties zu werden, T | undefinedwas ein sichereres Indexing an den Call-Sites erzwingt. Zudem wird empfohlen, oneOf alleine zu nutzen, statt es mit zusätzlicher Composition zu mischen, und $defs bei unklarer Platzierung an der Root zu halten, da falsch platzierte Definitionen im generierten Output verschwinden können. Ein weiteres Detail spart später Zeit: openapi-typescript wird niemals erzeugen anyerzeugen, sodass fehlende Schema-Details frühzeitig auffallen, statt unter permissiven Typen verborgen zu bleiben.

Halte die Spec explizit, sonst spiegelt der Generator die Ambiguität einfach eins zu eins an dich zurück.

Der Workflow ist denkbar einfach: Die Spec unter Versionskontrolle legen, beim Build regenerieren, die generierte Datei committen und den Type Checker meckern lassen, bevor jemand einen Mismatch merget. So hast du eine stabile Contract-Boundary für den Rest der Pipeline.

Die Wahl: Pure Types, Full Clients oder kein Codegen

Muster Build-Zeit Output-Dateien Bundle-Größe Beste Wahl
Reine Types mit leichtem Wrapper Schnell Wenig Niedrig Teams, die Kontrolle und eine kleine Runtime-Surface wollen
Vollständiges Client-Codegen Langsamer Viele Höher Teams, die schnellen Handoff und autogenerierte Operations wollen
Keine Codegen-Request-Builder Schnell Keine oder minimale Niedrig Single-Codebase-Apps, die handgeschriebene Transport-Logik bevorzugen

Die Wahl ist nicht wirklich „welches Tool gewinnt“. Es geht darum, welche Pipeline-Struktur zu deinem Repo, deinem Team und dem Churn der API passt. In einem 2025er Benchmark mit einer großen OpenAPI-Spec von etwa 75.000 Zeilen, 2 MB und ca. 1.200 Operations, openapi-typescript generierter Output in etwa 1,5 Sekunden im Durchschnitt, verglichen mit etwa 8,0 Sekunden für @hey-api/openapi-ts, 5,5 Sekunden für Orval, und 18,1 Sekunden für Kubb, während dabei eine einzige Ausgabedatei erzeugt wird, im Gegensatz zu 16 für hey-api, 2,719 für Orval, und 3,877 für Kubb (Benchmark-Details).

Pure Types setzen auf Kontrolle

Ein reines Type-Setup harmoniert gut mit einer handgeschriebenen Request-Layer, da man die Runtime klein und die API Oberfläche simpel halten kann. Das ist wichtig bei bundler-sensitiven Frontends und in Apps, in denen ein Team sowohl die Spec als auch den Consumer verantwortet. Falls du eine Erinnerung brauchst, dass Developer Experience mehr als nur Syntax Sugar ist, hilft der Aspekt der Developer Experience sich leichter beurteilen, wenn dein Client code kurz, offensichtlich und reviewbar ist.

Vollständige Clients priorisieren Handoff-Speed

openapi-generator, hey-api, Orval, und Kubb versuchen alle, mehr als nur Types zu liefern. Das kann hilfreich sein, wenn man Request-Methoden, Models und Plumbing gemeinsam generieren möchte, besonders bei großen Handoffs zwischen Backend- und Frontend-Teams. Der Preis dafür ist im obigen Benchmark offensichtlich: mehr generierte Dateien, eine größere Runtime-Surface und mehr Potenzial für Build-Friction, sobald die Spec wächst.

Kein Codegen begünstigt lokale Refactors

Typisierte Request-Builder und fetch Wrapper funktionieren gut, wenn eine Codebase beide Enden der Struktur kontrolliert und die API Änderungen eng abgestimmt sind. Der Nachteil ist der Wartungsaufwand. Je mehr Teams und Repositories zwischen Producer und Consumer liegen, desto eher driftet eine manuell geschriebene Request-Layer ab, sofern man nicht aggressiv auf Contract Tests setzt.

Die Kernentscheidung ist nicht ideologisch. Wenn dein Bundle-Budget knapp ist, sind reine Typen attraktiv. Wenn dein Team maximales Scaffolding möchte und das Ergebnis verarbeiten kann, reduzieren vollständige Clients die Setup-Zeit. Wenn du minimale bewegliche Teile willst und den Contract nah am Code halten kannst, sind No-Codegen Request-Builder der richtige interne Kompromiss.

Einen Thin Typed Client um Fetch oder Axios bauen

Ein Diagramm, das den Prozess eines Typed Client Wrappers zeigt, der generierte TypeScript API Definitionen für Web-Requests nutzt.

Ein Thin Wrapper ist der Punkt, an dem der Generator aufhört und deine App-Logik code beginnt. Der Wrapper sollte pro Operation eine Funktion bereitstellen, typisierte Params und Query-Objekte akzeptieren und den Call weiterleiten an fetch oder eine injizierte axios Instanz, ohne dabei zu komplex zu werden. In den meisten Production-Setups bleibt diese Layer bei etwa 30–60 Zeilen, da die generierten Typen die Struktur bereits weitgehend abdecken.

Hier ist das bewährte mentale Modell:

  • Path-Params bleiben typisiert sodass /users/{id} can’t be called without an id.
  • Query-Objekte bleiben typisiert damit optionale Filter nicht in einem String-Salat enden.
  • Response-Bodies bleiben typisiert sodass das Parsen von code der erwarteten, präzisen Struktur vertrauen kann.

Ein solcher Wrapper ist absichtlich simpel gehalten. Er sollte keine Retries, Transforms oder Auth-Policies erfinden, wenn diese woanders hingehören. Er sollte die Anfrage lediglich von einer typisierten Operation in den Transport-Layer übergeben und das typisierte Ergebnis zurückgeben.

Halte den Wrapper simpel und schlank, sonst wirkt sich jede zukünftige Änderung am Codegen auf deine gesamte App aus.

Ein häufiger Fehler besteht darin, Unstimmigkeiten einfach mit as any wenn die generierten Typen nicht zur alten Wrapper-Signatur passen. Das sichert zwar einen grünen Build, führt aber zu einer fragilen App. Zudem verbirgt es genau den Contract-Bruch, den der Generator eigentlich aufzeigen sollte.

Für Teams, die Axios bevorzugen, ist das Muster identisch, nur die Transport-Implementierung ändert sich. Für Teams, die ein einfacheres code im Browser suchen, fetch ist oft ausreichend. Entscheidend ist, dass die Request-Funktion den generierten Path-Typ akzeptiert und eine typisierte Response zurückgibt, statt eines ungenauen Objekts, das erst später angepasst werden muss.

Wenn du diese Nahtstelle gut nutzt, openapi-typescript ermöglicht dir eine saubere Trennung der Zuständigkeiten. Das Schema liegt in der Spec, der Transport im Wrapper, und die App sieht typisierte Operationen statt ad-hoc code Requests.

Runtime-Validierung mit zod, ajv oder io-ts hinzufügen

TypeScript-Typen verschwinden zur Laufzeit, und das Netzwerk kümmert sich nicht darum, wie sicher sich dein Editor fühlt. Deshalb lautet das sichere Pattern nicht „Typen generieren und hoffen“, sondern „Typen generieren und dann an der Grenze validieren, wo unvertrauenswürdige Daten in die App gelangen“. Das generierte Schema bleibt die Source of Truth, und Validierungs-Libraries wie zod, ajv, und io-ts übernehmen die Boundary-Checks, die Compile-Time-Typen nicht abdecken können.

Validieren, wo die Daten eintreffen

Bei React-Apps liegt die Grenze meist direkt nach dem Request-Resolve und bevor der Payload in den State geht. Bei Servern ist das, bevor der Payload in eine Datenbank geschrieben oder an eine Business Rule übergeben wird. Die Regel ist simpel: Halte die Validierung nah an der Grenze und verstreue keine manuellen Checks über code.

Ein zod kann die Shape der generierten Response spiegeln, ohne sie zu ersetzen:

import { z } from "zod";

const WeatherForecastSchema = z.object({
  date: z.string(),
  temperatureC: z.number(),
  summary: z.string().nullable(),
  temperatureF: z.number().optional(),
});

Dieses Beispiel validiert die vom Schema als optional markierten Felder und hält den Runtime-Check konsistent mit dem Output des Generators. ajv ist eine starke Wahl, wenn du High-Throughput-JSON-Schema-Validierung auf dem Server willst, während io-ts passt immer noch zu Teams, die bereits im fp-ts Composition-Stil arbeiten.

Der größte Fehler ist eine zu späte Validierung. Wenn der Payload zuerst in deiner App landet, wurde das Typsystem bereits umgangen und der Bug hat einen Platz, sich zu verstecken. Ein kurzer Guide zu Unit Tests für JavaScript passt perfekt zu dieser Denkweise, da sowohl Unit Tests als auch Boundary Validation am besten funktionieren, wenn sie falsche Annahmen frühzeitig abfangen.

Das saubere Layering ist vorhersehbar. OpenAPI TypeScript erstellt den Contract, der Validator prüft den Payload zur Laufzeit, und deine App code sieht nur Daten, die beide Schritte überstanden haben. Das ist eine deutlich bessere Boundary, als einem statischen Typ zuzutrauen, eine nicht vertrauenswürdige Response zu kontrollieren.

Generation, Validation und Contract Tests in die CI bringen

Screenshot von https://github.com

Eine Pipeline, die Bestand hat, macht den Contract zum Gate, nicht zur Empfehlung. Typen neu generieren, bei Drift fehlschlagen, ausführen tsc --noEmit, und die API-Shape gegen ein Mock- oder Contract-Tool prüfen, bevor du mergst. Wenn du die Generator-Version in package.jsonfestschreibst, können zwei Engineers nicht versehentlich unterschiedliche Outputs aus derselben Spec erzeugen.

Ein einfaches GitHub Actions-Setup

Ein praktischer Workflow sieht so aus:

  1. Lade die Spec aus dem Repo oder der generierten Quelle.
  2. Regeneriere die Types.
  3. Job abbrechen, falls git diff Änderungen anzeigt.
  4. Ausführen tsc --noEmit.
  5. Führe einen Contract Test gegen einen Mock Server wie Prism oder einen Spectral-basierten Check aus.

Der Hauptunterschied zwischen Contract Tests und Snapshot Tests ist der Scope. Snapshots sagen dir oft nur, dass sich die Datei geändert hat. Contract Tests sagen dir, ob die Struktur noch so funktioniert, wie es die Spec vorgibt.

Ein Mock Server ist besonders nützlich, wenn Backend und Frontend zeitlich oder durch Teamgrenzen getrennt sind. Er bietet dem Consumer code eine vorhersehbare API Oberfläche, während er den tatsächlichen Contract prüft, statt nur ein hard-coded Fixture zu nutzen. Der Guide zum Continuous Integration Setup ist eine hilfreiche Referenz, falls dein Team noch eine saubere, reproduzierbare CI-Baseline benötigt.

Das Fixieren der Generator-Version verhindert einen der nervigsten Fehler in Codegen-Pipelines: unsichtbare Abweichungen im Output. Wenn ein Entwickler den Generator lokal aktualisiert und ein anderer nicht, wird die generierte Datei eher zur Fehlerquelle als zum verlässlichen Signal. Die CI sollte das unmöglich machen.

Das Ergebnis ist eine Pipeline, in der Schema-Änderungen, Type Generation, Compiler-Checks und Contract Tests einander verstärken. Genau das macht den Workflow zuverlässig.

Wartbare Pipelines, Performance und eine finale Checkliste

Eine Checkliste mit vier wichtigen Schritten zur Wartung einer OpenAPI TypeScript Pipeline für Softwareprojekte.

Die Pipelines, die langfristig funktionieren, sind die mit einer soliden Governance. Versioniere die Spec, review Schema-Änderungen wie code, pinne den Generator und dokumentiere, wie Breaking Changes freigegeben werden. Wenn der Prozess unklar ist, werden Leute ihn umgehen – und dann sind die generierten Types nur noch Deko statt echter Absicherung.

Einige Performance-Hebel sind wirklich entscheidend

Inkrementelle Generierung hilft in Monorepos, in denen sich die Spec oft ändert, aber nur ein Package sie nutzt. tsc --incremental kann redundante Compiler-Arbeit reduzieren. Zudem hält das Deaktivieren unnötiger Output-Flags in Production-Builds die generierte Fläche klein. In der Praxis ist der größte Gewinn jedoch eher organisatorisch als technisch, denn eine vorhersehbare Pipeline wird öfter genutzt als eine hochkomplexe.

Diese Checkliste solltest du griffbereit haben:

  • Version Pinning: Locke die openapi-typescript Version in package.json damit der Output auf allen Maschinen identisch bleibt.
  • Schema-Review: Behandle Spec-Änderungen als prüfbare Contract-Änderungen, nicht als bloßes Aufräumen.
  • Drift-Erkennung: Regenerate in CI and fail on diff.
  • Edge-Validierung: Nicht vertrauenswürdige Payloads parsen, bevor sie App-State oder Persistence erreichen.
  • Contract Testing: Führe einen Mock-basierten Check durch, der beweist, dass der Consumer code noch zum Schema passt.
  • Breaking-Change-Policy: Lege fest, wer Shape-Änderungen freigibt und wie Clients benachrichtigt werden.

Eine Pipeline mit diesen Gates generiert nicht nur Types, sie macht den Contract sichtbar. Diese Sichtbarkeit verhindert, dass Teams blind einer Datei vertrauen, die nur sicher aussieht.

Wenn du Capacitor- oder Electron-Apps auslieferst und willst, dass deine Update-Pipeline mit derselben Disziplin läuft, gibt dir Capgo einen praktischen Weg, JavaScript, CSS, Texte, Config und Asset-Fixes schnell auszurollen, ohne auf das App-Store-Review zu warten. Besuche Capgo um zu sehen, wie signierte Bundles, Rollback-Schutz und Release-Kontrollen in einen Release-Prozess passen, der Geschwindigkeit benötigt, ohne die Kontrolle zu verlieren.

Live-Updates für Capacitor-Apps

Wenn ein Bug im Web-Layer live ist, liefere den Fix über Capgo aus, statt Tage auf die App-Store-Freigabe zu warten. Nutzer bekommen das Update im Hintergrund, während native Änderungen im normalen Review-Prozess bleiben.

Neues aus unserem Blog

Capgo gibt dir die besten Insights, um eine wirklich professionelle Mobile App zu erstellen.