On peut généralement repérer le moment où un API pipeline commence à mentir à l'équipe. Un schéma change, les types générés s'actualisent sans plaintes, la PR passe vert, et puis quelqu'un en front-end continue de lire la forme de réponse ancienne parce que le wrapper a éliminé l'erreur. C'est le problème OpenAPI TypeScript, pas la question de savoir si un générateur peut cracher des interfaces.
La question utile est plus difficile. Quel contrat voulez-vous entre schéma, transport et validation, et lesquels parties devraient échouer rapidement en temps de build plutôt que de se faufiler en temps de exécution ? Une fois que vous avez encadré OpenAPI TypeScript comme choix de pipeline, les compromis deviennent beaucoup plus clairs, et les outils cesseront de prétendre être la solution complète.
Table des matières
- Pourquoi les types générés ne sont pas les mêmes qu'une API sécurisée
- Génération de types TypeScript à partir d'une spécification OpenAPI
- Choisir entre les types purs, les clients complets et sans génération de code
- Brancher un client mince autour de Fetch ou Axios
- Ajouter une validation en temps réel avec zod, ajv ou io-ts
- Mettre en place des tests de génération, de validation et de contrat dans CI
- Les files d'attente maintenables, les performances et un dernier checklist
Pourquoi les types générés ne sont pas les mêmes qu'un API sûr
Un collègue fusionne une PR qui ajoute un champ de réponse facultatif. Le fichier généré met à jour proprement, la diff est ennuyeuse, et tout le monde passe à autre chose. Ensuite, la partie front-end continue de lire une forme plus ancienne à travers un wrapper manuscrit qui a été « temporairement » cast avec as anyet la production commence à se comporter comme si le contrat n'avait jamais changé.
C'est là l'embûche avec les types générés. TypeScript ne peut protéger que le code qui consomme les types généréset seulement si la couche de transport ne supprime pas à nouveau le contrat. Le côté OpenAPI vous donne un schéma, pas la garantie que chaque appelant le respecte. La discussion autour de la compréhension des API connections est utile ici car elle pousse la conversation loin d'une seule outil vers la manière dont les systèmes se connectent.
Où les échecs se cachent
Les ruptures les plus courantes sont ennuyeuses, pas exotiques. Le dérive du schéma se produit lorsque la spécification OpenAPI et le service déployé cesseront de correspondre. La couverture partielle apparaît lorsque la spécification ne modélise que la voie heureuse, tandis que l'application repose sur des cas d'extrémité non documentés. Les enveloppes manuscrites sont souvent là où les types se dégradent, surtout lorsque quelqu'un veut « se déplacer rapidement » et utilise any ou un cast de réponse lâche.
Règle pratique : si l'enveloppe peut mentir, le générateur ne peut pas vous sauver.
There est également un décalage de runtime. Les types TypeScript disparaissent après compilation, ils ne peuvent donc pas rejeter un JSON malformé qui arrive par le câble. Le réseau ne se soucie pas de ce que votre éditeur a inféré, et c'est pourquoi un client généré n'est qu'une couche dans un pipeline plus sûr API.
La question opérationnelle plus large est la sécurité et la discipline des contrats, et non seulement la commodité du développeur. Si vous souhaitez une vue structurée de la façon dont les contrats API s'intègrent dans un cycle de vie d'application plus large, ce guide interne sur les normes de sécurité API pour le respect des exigences des magasins d'applications est un compagnon utile.
La manière mature de penser à openapi typescript est celle-ci. Cela vous donne un pont strict de schéma à types, ce qui est excellent, mais il ne valide pas les requêtes, n'impose pas la forme du payload de runtime, ni arrête un wrapper maladroit qui remet tout en question. Le générateur est le 20 pour cent facile. Le reste est la conception du pipeline, et c'est là que les équipes gagnent confiance ou accumulent une confiance fausse.
Génération de types TypeScript à partir d'une spécification OpenAPI

La configuration la plus légère utile est généralement celle qui survit à un changement réel de répertoire. Gardez la spécification OpenAPI dans le même répertoire, générez un fichier de types commis, et faites visible la dérive dans CI au lieu de compter sur quelqu'un pour se souvenir d'une étape de rafraîchissement. Une commande comme npx openapi-typescript ./openapi.yaml -o ./src/types/api.d.ts vous donne un fichier de sortie déterministe que les réviseurs peuvent inspecter comme n'importe quel changement source.
Les drapeaux qui comptent vraiment
The -o flag de sortie est important car elle rend l'artefact généré explicite. --immutable est utile lorsque vous voulez que les types générés conservent l'intention readonly dans la sortie, et --alphabetize garde les différences stables lorsque l'ordre du schéma change sans signification sémantique. --enum est important lorsque votre équipe préfère les enums dans la surface générée au lieu des unions.
La documentation du projet est claire sur le champ, il s'agit d'un générateur de types, pas un runtime de client ou un niveau de requête, et cette limitation aide lorsque vous voulez une mise en place légère, type-first. Son dépôt montre également le modèle de maintenance derrière l'outil, ce qui est l'une des raisons pour lesquelles les outils open source peuvent tenir en production lorsque les documents et les versions restent actifs, comme discuté dansle cas de la maintenance open source GitHub repository and CLI documentation.
__CAPGO_KEEP_0__ et __CAPGO_KEEP_1__ package.json alors que la commande vit à côté des autres scripts de construction, exécutez-la chaque fois que la spécification change. Dans CI, régénérez le fichier et échouez si git diff montre un décalage. Cela transforme les changements de contrat en travail de revue visible au lieu de risque silencieux en temps de exécution.
Le côté schéma compte tout autant que la ligne de commande. Le projet recommande compilerOptions.noUncheckedIndexedAccess alors que additionalProperties , qui impose un indexage plus sûr aux sites d'appel. Il recommande également d'utiliser T | undefinedseul plutôt que de le mélanger avec une composition supplémentaire, et de garder oneOf à la racine lorsque la mise en place est ambiguë, car les définitions mal placées peuvent disparaître de la sortie générée. Un autre détail économise du temps plus tard, $defs ne produira jamais openapi-typescript , donc les détails de schéma manquants sont mis en surface plus tôt au lieu d'être cachés sous des types permissifs. anyGardez la spécification explicite, ou le générateur exposera fidèlement l'ambiguïté à votre égard.
Le workflow qui dure est simple. Placez la spécification sous contrôle de version, régénérez à chaque build, commitez le fichier généré et laissez le vérificateur de types se plaindre avant que personne ne merge une incohérence. Cela vous donne une frontière de contrat stable pour le reste de la chaîne de pipeline.
__CAPGO_KEEP_0__
Choisir entre les types purs, les clients complets et sans génération de code
| Modèle | Temps de construction | Fichiers de sortie | Poids du paquet | Meilleure correspondance |
|---|---|---|---|---|
| Types purs avec un enveloppe fine | Rapide | Peu | Bas | Équipes qui veulent le contrôle et une surface de runtime petite |
| Génération de code client complète | Lent | Beaucoup | Plus élevé | Les équipes qui veulent une livraison rapide et des opérations générées automatiquement |
| Pas de constructeurs de requêtes de génération de code | Rapide | Aucune ou minimale | Faible | Les applications monocodage qui préfèrent la logique de transport écrite à la main |
La choix n’est vraiment pas « quelle outil gagne ». C’est quelle forme de pipeline convient à votre dépôt, à votre équipe et à combien de changements le API subit. Dans un benchmark de 2025 autour d'une grande spécification OpenAPI d'environ 75 000 lignes, 2 Mo, et environ 1 200 opérations, openapi-typescript sortie générée en environ 1,5 secondes en moyenne, comparé à environ 8,0 secondes pour @hey-api/openapi-ts, 5,5 secondes pour Orval, et 18,1 secondes pour Kubb, tout en produisant un seul fichier de sortie par rapport à 16 pour hey-api, 2,719 pour Orvalet 3,877 pour Kubb (détails de benchmark).
Les types purs favorisent le contrôle
Une mise en place de types purs se marie bien avec une couche de requête manuscrite car vous pouvez garder la surface de runtime minuscule et la surface API ennuyeuse. Cela compte dans les front-end sensibles au bundler et dans les applications où une seule équipe possède à la fois la spécification et le consommateur. Si vous avez besoin d'un rappel que l'expérience du développeur n'est pas juste du sucre de syntaxe, le l'angle de l'expérience du développeur est plus facile à juger lorsque votre client code est court, évident et révisable.
Les clients complets favorisent la vitesse de transfert de main
openapi-generator, hey-api, Orval, et Kubb tous essaient de faire plus que les types. Cela peut être utile lorsque vous voulez que les méthodes de requête, les modèles et les tuyaux soient générés ensemble, surtout dans une grande main entre les équipes backend et frontend. Le coût est évident dans le benchmark ci-dessus, plus de fichiers générés, plus de surface de runtime et plus de place pour la friction de construction à mesure que la spécification grandit.
Aucun codegen favorise les réfacteurs locaux
Les constructeurs de requêtes typés et fetch Les enveloppes fonctionnent bien lorsque le codebase propriétaire des deux extrémités de la forme et les API changements sont étroitement coordonnés. L'inconvénient est la discipline de maintenance. Plus les équipes et les dépôtoires se situent entre le producteur et le consommateur, plus il est probable que la couche de requête manuscrite dérive, à moins que vous n'imposiez des tests de contrat de manière agressive.
Le point décisif de base n'est pas idéologique. Si votre budget de bundle est serré, les types purs sont attractifs. Si votre équipe souhaite une mise en place maximale et peut absorber les résultats, les clients complets réduisent le temps de mise en place. Si vous voulez des parties mobiles minimales et pouvez garder le contrat proche, les constructeurs de requêtes sans génération peuvent être la bonne option de négociation interne.
Brancher un Client Épaissi Typé autour de Fetch ou Axios

Un enveloppe mince est là où le générateur s'arrête et votre application code commence. L'enveloppe devrait exposer une fonction par opération, accepter des paramètres et des objets de requête typés, et transmettre l'appel vers fetch ou une instance injectée axios sans essayer d'être trop créatif. Dans la plupart des configurations de production, cette couche reste autour 30–60 lignes puisqu'il reste déjà la plupart de la forme aux types générés.
Voici le modèle mental qui tient bon :
- Les paramètres de chemin restent typés afin que
/users/{id}ne peut pas être appelé sans unid. - Les objets de requête restent typés Ainsi, les filtres optionnels ne se transforment pas en bouillie de chaînes.
- Les corps de réponse restent typés Ainsi, la mise en forme de code peut faire confiance à la forme étroite qu'elle attend.
Un wrapper comme celui-ci est intentionnellement ennuyeux. Il ne doit pas inventer des réessais, des transformations ou des politiques d'autorisation si ceux-ci appartiennent àilleurs. Il doit déplacer la requête d'une opération typée dans la couche de transport et puis rendre le résultat typé à rebours.
Gardez le wrapper ennuyeux et léger en dépendances, ou chaque changement futur de génération de code se propagera dans votre application.
L'échec commun est de surmonter les incompatibilités avec as any lorsque les types générés ne correspondent pas à la signature du wrapper ancien. Cela achète une construction verte et une application fragile. Cela cache également la rupture de contrat que vous avez voulu que le générateur révèle.
Pour les équipes qui préfèrent Axios, le modèle est le même, seulement la mise en œuvre de transport change. Pour les équipes qui veulent une code côté navigateur plus simple fetch est souvent suffisant.
La partie importante est que la fonction de requête accepte le type de chemin généré et retourne une réponse typée, et non un objet de forme lâche qui doit être massé ultérieurement. typescript openapi vous donne une division claire du travail. Le schéma vit dans la spécification, le transport vit dans l' enveloppe, et l'application voit des opérations typées au lieu de requêtes ad hoc code.
Ajouter la validation en temps de runtime avec zod, ajv ou io-ts
Les types TypeScript disparaissent à la runtime, et le réseau ne s'intéresse pas à la confiance de votre éditeur. C'est pourquoi le modèle sûr n'est pas « générer des types et espérer », c'est « générer des types, puis valider à l'extrémité où les données non fiables pénètrent dans l'application ». Le schéma généré reste la source de vérité, et les bibliothèques de validation comme zod, ajvet io-ts gèrent les contrôles de limite qui ne peuvent pas être effectués par les types de compilation.
Valider où les données pénètrent
Pour les applications React, l'extrémité est généralement juste après que la requête se soit résolue et avant que le payload ne pénètre dans l'état. Pour les serveurs, c'est avant que le payload ne soit écrit dans une base de données ou remis à une règle commerciale. La règle est simple, gardez la validation proche de la limite et ne dispersiez pas les vérifications manuelles à travers les fonctionnalités code.
Un zod la forme peut refléter la forme de réponse générée sans la remplacer :
import { z } from "zod";
const WeatherForecastSchema = z.object({
date: z.string(),
temperatureC: z.number(),
summary: z.string().nullable(),
temperatureF: z.number().optional(),
});
Cet exemple valide les champs que le schéma a marqués comme optionnels, et il garde la vérification en temps de runtime alignée avec ce que le générateur a produit. ajv est un choix fort quand vous voulez une validation de forme JSON haute performance sur le serveur, tout en gardant io-ts encore convient aux équipes qui vivent déjà dans le fp-ts le style de composition.
La grande erreur est de valider trop tard. Si le payload pénètre dans votre application en premier, le système de types a déjà été contourné et la bug a un endroit où se cacher. Un guide court sur les tests unitaires pour JavaScript s'est bien avec cette mentalité, car les tests unitaires et la validation des limites fonctionnent mieux lorsque ils détectent les mauvaises hypothèses tôt.
La couche de mise en forme est prévisible. OpenAPI TypeScript genère le contrat, le validateur vérifie le payload de runtime, et votre application code ne voit que les données qui ont survécu à ces deux étapes. C'est un meilleur seuil de limite que de faire confiance à un type statique pour policier une réponse non fiable.
Intégrer la génération, la validation et les tests de contrat dans CI

Un pipeline qui dure transforme le contrat en une porte, et non en une suggestion. Regénérer les types, échouer sur le dérive, exécuter tsc --noEmit, et exercez la forme API contre un outil de contrat ou de mock avant la fusion. Si vous fixez la version du générateur dans package.jsonDeux ingénieurs ne peuvent pas produire accidentellement des sorties différentes à partir de la même spécification.
Une forme d'actions simple GitHub
Un flux de travail pratique ressemble à ceci :
- Extraire la spécification à partir du dépôt ou de la source générée.
- Régenerer les types.
- Échouer à la tâche si
git diffmontre des changements. - Démarer
tsc --noEmit. - Exécuter un test de contrat contre un serveur de simulation tel que Prism ou un contrôle Spectral.
La principale différence entre les tests de contrat et les tests de snapshot est le champ. Les snapshots vous disent souvent que le fichier a changé. Les tests de contrat vous disent si la forme continue à se comporter comme la spécification le dit.
Un serveur de simulation est particulièrement utile lorsque le travail backend et frontend sont séparés par des frontières de temps ou d'équipe. Il donne à code un surface prévisible API tout en vérifiant le contrat réel plutôt qu'un fixture fixé. guide de configuration de l'ensemble de l'intégration continue est une référence utile si votre équipe a encore besoin d'une base de CI propre et répétable.
En fixant la version du générateur, on évite l'une des pires erreurs dans les pipelines de génération de code, la dérive de sortie invisible. Si un développeur met à jour le générateur localement et un autre ne le fait pas, le fichier généré peut devenir une source de bruit aléatoire au lieu de signal. La CI devrait rendre cela impossible.
Le résultat est un pipeline où les modifications de schéma, la génération de types, les vérifications du compilateur et les tests de contrat se renforcent mutuellement. C'est ce qui fait que le workflow est honnête.
Pipelines Gérables, Performance et Liste de Contrôle finale

Les pipelines qui survivent sont ceux avec une gouvernance banale. Versionnez la spécification, passez en revue les modifications de schéma comme code, fixez la version du générateur et documentez comment les changements brisants sont approuvés. Si le processus est flou, les gens contourneront, et les types générés deviendront des décorations au lieu d'une mise en œuvre.
Un ou deux leviers de performance ont vraiment de l'importance
La génération incrémentale aide dans les monorepos où la spécification change souvent mais seulement un package la consomme. tsc --incremental Peut éliminer le travail de compilateur répété, et désactiver les drapeaux de sortie que vous n'avez pas besoin dans les builds de production garde la surface générée plus petite. En pratique, le plus grand gain est toujours social, et non technique, car un pipeline prévisible est exécuté plus souvent qu'un pipeline astucieux.
Le tableau de contrôle ci-dessous est celui qu'il faut garder à l'esprit :
- Fixation de version : Bloquez la
openapi-typescriptversion danspackage.jsonAinsi, l'output ne dérive pas d'un ordinateur à l'autre. - Révision du schéma : Traitez les changements de spécification comme des changements de contrat révisables, et non comme des tâches de maintenance.
- Détection de dérive : Régeneratez dans CI et échouez si des différences sont détectées.
- Validation de bord : Analysez les payloads non fiables avant qu'elles ne parviennent à l'état d'application ou à la persistance.
- Test de contrat : Exécutez une vérification avec un mock qui prouve que le consommateur code correspond toujours au schéma.
- Politique de changement de rupture : Notez qui approuve les changements de forme et comment les clients sont informés.
Un pipeline qui inclut ces portes ne génère pas seulement des types, il rend le contrat visible. Cette visibilité est ce qui empêche les équipes de faire confiance à un fichier qui ne semble pas dangereux.
Si vous envoyez des applications Capacitor ou Electron et que vous voulez que votre pipeline de mise à jour se comporte avec la même discipline, Capgo vous donne un moyen pratique de déplacer rapidement les corrections JavaScript, CSS, copie, configuration et actifs sans attendre la revue de l'App Store. Capgo __CAPGO_KEEP_0__