23. Februar 2026
Typsichere API-Codegenerierung für React in 2026
Das Ökosystem hat sich gefunden. Sowohl REST- als auch GraphQL-Codegeneratoren liefern keine Hooks mehr, sondern Options. Hier ist das vollständige Bild.
Sascha Becker
Author12 Min. Lesezeit

Typsichere API-Codegenerierung für React in 2026
Jedes Jahr stelle ich mir dieselbe Frage: Was ist aktuell der beste Weg, typsicheren und ergonomischen Frontend-Code aus meinen API-Definitionen zu generieren? Die Antwort ändert sich ständig. Das hier ist die 2026er-Ausgabe.
Die Kurzversion: Das Ökosystem hat konvergiert. Sowohl die REST- als auch die GraphQL-Welt sind unabhängig voneinander zum selben Schluss gekommen — keine framework-spezifischen Hooks mehr generieren, stattdessen framework-agnostische Options und typisierte Dokumente.
Wenn du bisher generierte useGetPet()- oder useFilmsQuery()-Hooks verwendet hast, wird es Zeit zu verstehen, warum dieses Pattern ausläuft und was es ersetzt hat.
Warum der Wechsel weg von generierten Hooks?
Das ist die wichtigste Veränderung in beiden Ökosystemen, also klären wir das direkt.
Früher haben Codegeneratoren für jeden Endpoint oder jede Query einen eigenen React-Hook produziert. Ein REST-Generator lieferte useGetPetById(), ein GraphQL-Generator useFilmsQuery(). Komfortabel? Absolut. Nachhaltig? Nein.
Die Probleme haben sich aufgestapelt:
Kombinatorischer Wartungsaufwand. Jede Kombination aus HTTP-Client (Axios, Fetch, Ky) × Data-Fetching-Library (TanStack Query, SWR, Apollo, urql) × Framework (React, Vue, Svelte, Solid, Angular) brauchte ein eigenes Plugin. Das GraphQL-Code-Generator-Team hat Dutzende davon gepflegt, jedes mit eigenen Konfigurationsmacken und Inkonsistenzen.
Framework-Lock-in im generierten Output. Generierte Hooks koppeln deine API-Schicht an React. Wenn dein Team auch eine Vue-App pflegt oder zu Solid migriert, bricht die gesamte Codegen-Pipeline zusammen.
Kompositionsprobleme. Reacts Rules of Hooks bedeuten, dass man einen generierten Hook nicht in einer Schleife oder bedingt aufrufen kann. Man kann sie auch nicht einfach mit useQueries() verwenden, das ein Array von Option-Objekten erwartet — keine Hook-Aufrufe.
Unnötige Abstraktionsschicht. Ein generierter Hook ist nur ein dünner Wrapper um useQuery({ queryKey, queryFn }). Seit TanStack Query v5 den queryOptions()-Helper eingeführt hat, ist dieser Wrapper überflüssiger Overhead.
Das GraphQL-Code-Generator-Team hat diesen Wechsel ausführlich in der v3/v5-Roadmap und der Client-Preset-Diskussion besprochen. Das Hey-API-Team hat sein TanStack-Query-Plugin von Anfang an nach dieser Philosophie gebaut (Issue #653).
Die REST-Seite: OpenAPI-Codegenerierung
@hey-api/openapi-ts
Hey API ist der aktuelle Spitzenreiter für OpenAPI-zu-TypeScript-Generierung. Es ist der Nachfolger von openapi-typescript-codegen, komplett mit Plugin-basierter Architektur neu geschrieben.
Was es generiert:
- Typsichere SDK-Funktionen für jeden Endpoint
queryOptions/mutationOptions/infiniteQueryOptions-Funktionen für TanStack Query- Query-Key-Funktionen für Cache-Management
- Optionale Zod- oder Valibot-Validierungsschemas
Was es nicht generiert: Hooks.
Konfiguration
openapi-ts.config.tsimport { defineConfig } from "@hey-api/openapi-ts";export default defineConfig({input: "https://api.example.com/openapi.json",output: "src/client",plugins: ["@hey-api/typescript", "@hey-api/sdk", "@tanstack/react-query"],});
Ausführen:
bashnpx @hey-api/openapi-ts
Generierter Output
Der Generator produziert Options-Funktionen — einfache Funktionen, die Objekte zurückgeben:
ts// Generiert: src/client/@tanstack/react-query.gen.tsexport const getPetByIdOptions = (options: { path: { petId: number } }) => ({queryKey: getPetByIdQueryKey(options),queryFn: () => getPetById(options),});export const addPetMutation = () => ({mutationFn: (options: { body: { name: string } }) => addPet(options),});
Verwendung in Komponenten
Du spreadest die generierten Options in die Hooks von TanStack Query:
tsximport { useQuery, useMutation } from "@tanstack/react-query";import {getPetByIdOptions,addPetMutation,} from "./client/@tanstack/react-query.gen";function PetDetail({ petId }: { petId: number }) {const { data } = useQuery({...getPetByIdOptions({ path: { petId } }),staleTime: 5000, // beliebige zusätzliche Options});const mutation = useMutation({...addPetMutation(),onSuccess: () => {// Invalidieren, Weiterleiten, was auch immer nötig ist},});return <div>{data?.name}</div>;}
Dieses Pattern hat einen subtilen aber mächtigen Vorteil: Du kannst dieselben Options mit queryClient.prefetchQuery() für SSR, queryClient.getQueryData() für Cache-Reads und useQueries() für parallele Fetches verwenden — alles voll typisiert.
ts// Prefetch auf dem Server (Next.js)await queryClient.prefetchQuery(getPetByIdOptions({ path: { petId } }));// Aus dem Cache lesen — der Rückgabetyp wird inferiertconst cached = queryClient.getQueryData(getPetByIdQueryKey({ path: { petId } }),);
HTTP-Clients
Hey API unterstützt Fetch (Standard), Axios, Ky und framework-spezifische Clients wie Next.js und Nuxt. Das TanStack-Query-Plugin funktioniert mit React, Vue, Svelte, Solid und Angular — alles aus demselben generierten Output.
Orval
Orval ist der andere große Player. Im Gegensatz zu Hey API generiert Orval standardmäßig immer noch eigene Hooks. Einen useListPets()-Hook, einen useGetPetById()-Hook und so weiter.
orval.config.tsimport { defineConfig } from "orval";export default defineConfig({petstore: {input: "./petstore.yaml",output: {target: "./src/api/endpoints.ts",client: "react-query",mock: true, // generiert MSW-Handler mit Faker.js},},});
Orvals großer Differenzierungspunkt ist die eingebaute Mock-Generierung. Es produziert MSW-Request-Handler mit realistischen Fake-Daten out of the box, was für die Frontend-Entwicklung gegen noch nicht existierende APIs hervorragend ist.
Es gibt einen offenen Feature Request für queryOptions-basierten Output. Wenn du ein neues Projekt startest, würde ich Hey API für den Options-basierten Ansatz empfehlen. Wenn du Orval bereits nutzt und Mock-Generierung brauchst, ist es immer noch eine solide Wahl.
Direktvergleich
| @hey-api/openapi-ts | Orval | |
|---|---|---|
| Output | Options-Objekte | Eigene Hooks |
| TanStack Query | React, Vue, Svelte, Solid, Angular | React, Vue, Svelte, Solid |
| Mock-Generierung | Separates Plugin | Eingebaut (MSW + Faker.js) |
| Validierung | Zod, Valibot | Zod |
| HTTP-Clients | Fetch, Axios, Ky, Next.js, Nuxt | Axios, Fetch |
| Reife | Pre-1.0, schnelle Entwicklung | v8, stabil |
Die GraphQL-Seite
graphql-codegen mit dem Client Preset
GraphQL Code Generator von The Guild ist das etablierte Tool. Der empfohlene Ansatz ist das Client Preset, das typisierte Document-Objekte generiert — keine Hooks.
Was sich geändert hat
Früher hat man @graphql-codegen/typescript-react-apollo oder @graphql-codegen/typescript-react-query installiert und generierte Hooks bekommen. Diese Plugins sind jetzt deprecated und in Community-Repos ausgelagert. Die offizielle Empfehlung ist:
- Das Client Preset nutzen, um eine typisierte
graphql()-Funktion zu generieren - Queries inline mit dieser Funktion schreiben
- Die typisierten Dokumente an die Hooks der eigenen Client-Library übergeben
Konfiguration
codegen.tsimport type { CodegenConfig } from "@graphql-codegen/cli";const config: CodegenConfig = {schema: "https://api.example.com/graphql",documents: ["src/**/*.{ts,tsx}"],ignoreNoDocuments: true,generates: {"./src/gql/": {preset: "client",config: {documentMode: "string",enumsAsTypes: true,},presetConfig: {fragmentMasking: true,},},},};export default config;
Verwendung mit Apollo Client
Apollo Client versteht TypedDocumentNode nativ, die Integration ist nahtlos:
tsximport { useQuery } from "@apollo/client";import { graphql } from "./gql";const AllFilmsQuery = graphql(`query AllFilms {allFilms {films {titlereleaseDate}}}`);function Films() {// data ist voll typisiert — keine Generics nötigconst { data } = useQuery(AllFilmsQuery);return (<ul>{data?.allFilms?.films?.map((film) => (<li key={film?.title}>{film?.title}</li>))}</ul>);}
Verwendung mit TanStack Query
TanStack Query hat keinen nativen GraphQL-Support, also schreibt man einmal eine kleine execute-Funktion:
tsimport type { TypedDocumentString } from "./gql/graphql";export async function execute<TResult, TVariables>(query: TypedDocumentString<TResult, TVariables>,variables?: TVariables,): Promise<TResult> {const response = await fetch("/graphql", {method: "POST",headers: { "Content-Type": "application/json" },body: JSON.stringify({ query: query.toString(), variables }),});const { data } = await response.json();return data;}
Dann in Komponenten verwenden:
tsximport { useQuery } from "@tanstack/react-query";import { graphql } from "./gql";import { execute } from "./graphql-client";const PeopleQuery = graphql(`query AllPeople {allPeople {totalCountpeople {name}}}`);function People() {const { data } = useQuery({queryKey: ["allPeople"],queryFn: () => execute(PeopleQuery),});return <span>{data?.allPeople?.totalCount} Personen</span>;}
Fragment Masking
Eines der besten Features des Client Presets ist Fragment Masking. Es erzwingt, dass Komponenten nur auf die Daten zugreifen können, die sie explizit per Fragment deklariert haben:
tsximport { graphql, FragmentType, useFragment } from "./gql";const FilmCardFragment = graphql(`fragment FilmCard on Film {titlereleaseDatedirector}`);function FilmCard(props: { film: FragmentType<typeof FilmCardFragment> }) {const film = useFragment(FilmCardFragment, props.film);// film.title — typisiert und zugänglich// film.id — Compile-Error, nicht im Fragmentreturn <h3>{film.title}</h3>;}
Das ist ein Gamechanger für große Codebases. Es verhindert, dass Komponenten von Daten abhängen, die sie nicht angefragt haben, was das Refactoring von Queries sicher macht.
Apollo-Client-Nutzer
Apollos eigene Dokumentation rät davon ab, das Client Preset mit
Apollo-Client-Apps zu verwenden, da es zusätzlichen Runtime-Code generiert,
der mit Apollo inkompatibel ist. Wenn du Apollo nutzt, ziehe die
Legacy-Plugins typescript + typescript-operations in Betracht. Siehe die
Apollo-Docs
für das empfohlene Setup.
gql.tada: Ganz ohne Codegen
gql.tada geht einen radikal anderen Weg: kein Codegen-Schritt. Stattdessen nutzt es TypeScripts Typsystem, um Ergebnis- und Variablentypen zur Compile-Time aus deinen GraphQL-Queries zu inferieren.
Wie es funktioniert
- Dein Schema wird über ein TypeScript-Plugin geladen
- Das Plugin generiert eine
graphql-env.d.ts-Typdatei - Wenn du
graphql('query { ... }')schreibst, parst TypeScript den Query-String auf Typebene - Ergebnis- und Variablentypen werden inferiert — kein Build-Schritt, kein Watcher
Setup
bashnpm install gql.tada
tsconfig.json{"compilerOptions": {"plugins": [{"name": "gql.tada/ts-plugin","schema": "./schema.graphql","tadaOutputLocation": "./src/graphql-env.d.ts"}]}}
Verwendung
tsximport { graphql } from "gql.tada";const PokemonQuery = graphql(`query Pokemon($name: String!) {pokemon(name: $name) {idnametypes}}`);// Der Ergebnistyp wird vollständig inferiert:// { pokemon: { id: string; name: string; types: string[] } | null }
Du verwendest das typisierte Dokument mit jedem Client — urql, Apollo oder einem einfachen fetch-Wrapper mit TanStack Query. Der Hauptunterschied zu graphql-codegen: Es gibt keinen Build-Schritt. Deine Typen sind immer aktuell, weil sie von TypeScript selbst berechnet werden.
Wann gql.tada vs. graphql-codegen?
| gql.tada | graphql-codegen client-preset | |
|---|---|---|
| Codegen-Schritt | Keiner | Erforderlich (CLI oder Watcher) |
| Typen immer aktuell | Ja (von TS berechnet) | Erst nach Codegen-Lauf |
| Fragment-Handling | Explizit (als Argument übergeben) | Global (automatisch erkannt) |
| Persisted Documents | Via CLI | Eingebaut |
| Ökosystem-Reife | Neuer | Sehr ausgereift (~5M Downloads/Woche) |
| Editor-DX | Auto-Complete via TS-Plugin | Erfordert Codegen-Lauf für Typen |
| Große Schemas | Kann TS-Server verlangsamen | Kein TS-Performance-Impact |
Performance-Hinweis
Bei sehr großen Schemas kann gql.tadas Type-Level-Inferenz den TypeScript-Language-Server verlangsamen. Wenn dein Schema hunderte Typen und tief verschachtelte Queries hat, bietet graphql-codegen möglicherweise eine flüssigere Editor-Erfahrung, da die Typen vorberechnet sind.
Der Kleber: TanStack Query v5
Der Wechsel von Hooks zu Options wurde durch TanStack Query v5 ermöglicht, das den queryOptions()-Helper eingeführt hat:
tsimport { queryOptions } from "@tanstack/react-query";const todosOptions = queryOptions({queryKey: ["todos"],queryFn: fetchTodos,staleTime: 5000,});// In einem Hook verwendenconst { data } = useQuery(todosOptions);// Zum Prefetching verwendenawait queryClient.prefetchQuery(todosOptions);// Aus dem Cache lesen — voll typisiertconst cached = queryClient.getQueryData(todosOptions.queryKey);
Das ist nicht nur eine Convenience-Funktion. Es ist eine typsichere Options-Factory, die sicherstellt, dass queryKey, queryFn, Rückgabetypen und Cache-Typen synchron bleiben. Es ist das Primitiv, das Codegeneratoren jetzt ansteuern, anstatt eigene Hooks zu generieren.
Dasselbe Pattern existiert für Mutations (mutationOptions()) und Infinite Queries (infiniteQueryOptions()).
Meine Empfehlung für 2026
Für REST / OpenAPI
Nutze @hey-api/openapi-ts mit dem TanStack-Query-Plugin. Der Options-basierte Ansatz ist sauber, kompositionsfähig und framework-agnostisch. Die DX ist hervorragend: Du bekommst volle Typsicherheit von deiner OpenAPI-Spec bis zur data-Property deiner Komponente.
openapi-ts.config.tsimport { defineConfig } from "@hey-api/openapi-ts";export default defineConfig({input: "./openapi.yaml",output: "src/client",plugins: ["@hey-api/typescript", "@hey-api/sdk", "@tanstack/react-query"],});
Füge es zu deinen package.json-Scripts hinzu:
json{"scripts": {"codegen:api": "openapi-ts"}}
Für GraphQL
Wenn dein Schema klein bis mittelgroß ist, probiere gql.tada. Die Zero-Codegen-Erfahrung ist unschlagbar für Entwicklungsgeschwindigkeit.
Wenn dein Schema groß ist oder du Persisted Documents und Fragment Masking in einem ausgereiften Setup brauchst, nutze graphql-codegen mit dem Client Preset.
In beiden Fällen: Vermeide die alten Hook-generierenden Plugins. Sie werden bestenfalls von der Community gepflegt und sind schlimmstenfalls veraltet.
Validierungsschicht
Sowohl Hey API als auch graphql-codegen unterstützen die Generierung von Zod-Schemas aus deinen API-Definitionen. Das gibt dir Runtime-Validierung zusätzlich zu Compile-Time-Typen — nützlich an Systemgrenzen, wo man den Daten nicht vertrauen kann.
ts// openapi-ts.config.ts — Zod-Plugin hinzufügenplugins: ['@hey-api/typescript','@hey-api/sdk','@tanstack/react-query','@hey-api/zod', // Runtime-Validierungsschemas],
Das Pattern auf einen Blick
Hier ist das mentale Modell:
REST:
OpenAPI-Spec →
@hey-api/openapi-ts→ Options-Objekte →useQuery({ ...options })
GraphQL (mit Codegen):
GraphQL-Schema →
graphql-codegen→ TypedDocumentNode →useQuery(document)
GraphQL (ohne Codegen):
GraphQL-Schema →
gql.tada→ inferierte Typen →useQuery({ queryFn: () => execute(document) })
Alle drei Wege enden gleich: ein framework-agnostisches Primitiv, das sich in die vorhandenen Hooks deiner Data-Fetching-Library einsteckt. Der Generator kümmert sich um Typsicherheit und API-Mapping. Dein Framework kümmert sich um Rendering und State.
Das ist die Separation of Concerns, auf die sich das Ökosystem 2026 geeinigt hat.
Changelog
Dieser Artikel wird jährlich aktualisiert, um die neuesten Tools und Patterns widerzuspiegeln.
| Jahr | Wichtige Änderungen |
|---|---|
| 2026 | Erstausgabe. @hey-api/openapi-ts Options-Pattern, graphql-codegen Client Preset, gql.tada, Wechsel von Hooks zu Options. |
Quellen & Weiterführendes
- @hey-api/openapi-ts Dokumentation
Offizielle Docs für den führenden OpenAPI-zu-TypeScript-Codegenerator.
- Hey API — TanStack Query Plugin
Wie man queryOptions und mutationOptions aus der OpenAPI-Spec generiert.
- GraphQL Code Generator — Client Preset
Der empfohlene Codegen-Ansatz für GraphQL in 2026.
- GraphQL Code Generator — v3/v5-Roadmap
Das GitHub-Issue, in dem The Guild erklärt hat, warum sie von generierten Hooks abgerückt sind.
- gql.tada Dokumentation
Typsicheres GraphQL ohne Codegen-Schritt, mittels TypeScript-Inferenz.
- TanStack Query v5 — Query Options
Der queryOptions()-Helper, der das Options-basierte Codegen-Pattern ermöglicht hat.
- Orval Dokumentation
OpenAPI-Codegenerator mit eingebauter MSW-Mock-Generierung.
- Orval — queryOptions Feature Request
Community-Diskussion über das Hinzufügen von Options-basiertem Output zu Orval.
