Vous pouvez 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 en 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 d'exécution ? Une fois que vous avez encadré OpenAPI TypeScript As un 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 avec des types autour de Fetch ou Axios
- Ajouter une validation en temps réel avec zod, ajv ou io-ts
- Intégrer la génération, la validation et les tests de contrat dans CI
- Les pipelines maintenables, les performances et une liste de vérification finale
Pourquoi les types générés ne sont pas les mêmes qu'un API sûr
Un collègue fusionne un PR qui ajoute un champ de réponse optionnel. Le fichier généré se met à jour proprement, la diff est ennuyeuse, et tout le monde continue. Puis la partie front-end continue à 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'un outil unique et vers la manière dont les systèmes se connectent.
Où les échecs se cachent
Les ruptures les plus courantes sont ennuyeuses, pas exotiques. La dérive de 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 le chemin heureux, 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 mal formé qui arrive par le câble. Le réseau ne s'intéresse pas à 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 la confiance ou accumulent une confiance fausse.
La 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'un pas 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 avant tout. Son dépôt montre également le modèle de maintenance derrière l'outil, ce qui est partie de la raison pour laquelle les outils open source peuvent tenir dans la production lorsque les documents et les versions restent actifs, comme discuté dans le cas de la maintenance open source. Une lecture pratique de cette posture est le dépôt et la documentation de GitHub repository and CLI documentation.
__CAPGO_KEEP_1__ de __CAPGO_KEEP_2__ et la génération de câbles dans package.json alors que la commande vit à côté des autres scripts de construction, exécutez-la chaque fois que les spécifications changent. Dans CI, régénérez le fichier et échouez si git diff montre un décalage. Cela transforme les modifications de contrat en travail de revue visible au lieu de risque silencieux en temps de exécution.
La partie schéma compte autant que la ligne de commande. Le projet recommande compilerOptions.noUncheckedIndexedAccess devenir additionalProperties , qui impose un indexage plus sûr aux sites de 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 les spécifications explicites, ou le générateur exposera fidèlement l'ambiguïté à votre égard.
Le workflow qui dure est simple. Mettez les spécifications sous contrôle de version, régénérez à chaque build, commitez le fichier généré et laissez le vérificateur de types protester 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.
so the command lives beside the rest of the build scripts, then run it whenever the spec changes. In CI, regenerate the file and fail if __CAPGO_KEEP_0__ shows drift. That turns contract changes into visible review work instead of silent runtime risk. The schema side matters just as much as the command line. The project recommends __CAPGO_KEEP_1__ so __CAPGO_KEEP_2__ become __CAPGO_KEEP_3__, which forces safer indexing at call sites. It also recommends using __CAPGO_KEEP_4__ by itself rather than mixing it with extra composition, and keeping __CAPGO_KEEP_5__ at the root when placement is ambiguous, because misplaced definitions can disappear from generated output. One more detail saves time later, __CAPGO_KEEP_6__ will never produce __CAPGO_KEEP_7__, so missing schema detail gets surfaced early instead of hidden under permissive types. Keep the spec explicit, or the generator will faithfully expose the ambiguity back to you. The workflow that lasts is straightforward. Put the spec under version control, regenerate on build, commit the generated file, and let the type checker complain before anyone merges a mismatch. That gives you a stable contract boundary for the rest of the pipeline.
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 bundle | Meilleure correspondance |
|---|---|---|---|---|
| Types purs avec un enveloppe fine | Rapide | Peu | Bas | Équipes qui veulent contrôler et petite surface de runtime |
| Génération de code client complète | Langue plus lente | Beaucoup | Plus élevé | Les équipes qui veulent une livraison rapide et des opérations générées automatiquement |
| Aucune demande de constructeurs de codegen | Rapide | Aucune ou minimale | Basse | Les applications monocodage qui préfèrent une 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 s'accorde bien avec une couche de requête manuscrite car vous pouvez garder la surface de runtime minuscule et la surface de API ennuyeuse. Cela compte dans les front-ends 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 de 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.
Pas de génération de code favorise les réfacteurs locaux
Les constructeurs de requêtes typés et fetch Les enveloppes fonctionnent bien lorsque le codebase propriétaire possède les deux extrémités de la forme et que les modifications de API sont étroitement coordonnées. L'inconvénient est la discipline de maintenance. Plus les équipes et les dépôtoires se situent entre producteur et 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 central 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 Étroitement Typé autour de Fetch ou Axios

Un enveloppe mince est là où le générateur s'arrête et où 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 à fetch ou une instance injectée axios sans essayer d'être trop créatif. Dans la plupart des configurations de production, ce niveau reste autour 30–60 lignes car les types générés portent déjà la plupart de la forme.
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 que les filtres optionnels ne se transforment pas en bouillie de chaînes.
- Les corps de réponse restent typés so parsing code can trust the narrow shape it expects.
Un wrapper comme celui-ci est intentionnellement ennuyeux. Il ne devrait pas inventer des réessais, des transformations ou des politiques d'autorisation si ceux-ci appartiennent àilleurs. Il devrait déplacer la requête d'une opération typée dans la couche de transport et puis remettre le résultat typé en haut.
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 se fait masser plus tard.
Si vous utilisez bien cette articulation, typescript ouverteapi vous obtenez 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 de temps de fonctionnement avec zod, ajv ou io-ts
Les types TypeScript disparaissent à l'exécution, 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 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 traitent les vérifications de limites que les types de compilation ne peuvent pas.
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 s'est résolue et avant que le payload n'entre 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 de temps de fonctionnement alignée avec ce que le générateur a produit. ajv est un choix fort lorsque vous voulez une validation de forme JSON haute performance sur le serveur, tandis que io-ts encore convient aux équipes qui vivent déjà dans le fp-ts le style de composition.
L'erreur majeure 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 dès le début.
La couche de mise en forme est prévisible. OpenAPI TypeScript génère le contrat, le validateur vérifie le payload en temps de cours, et votre application code ne voit que les données qui ont survécu à ces deux étapes. C'est un bord de limite bien meilleur que de se fier à un type statique pour faire respecter une réponse non fiable.
Intégrer la génération, la validation et les tests de contrat dans la CI

Une chaîne de pipeline durable transforme le contrat en une barrière, et non en une suggestion. Regénérer les types, échouer en cas de dérive, exécuter tsc --noEmit, et exercez la forme API contre un outil de contrat ou un mock avant la fusion. Si vous fixez la version du générateur dans package.jsonDeux ingénieurs ne peuvent pas produire par accident des sorties différentes à partir de la même spécification.
Une forme d'actions simple GitHub
Un workflow 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 au travail si
git diffmontre des changements. - Exécuter
tsc --noEmit. - Exécuter un test de contrat contre un serveur de mock 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 captures d'écran 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 mock est particulièrement utile lorsque le travail backend et frontend sont séparés par le temps ou les limites de l'équipe. Il donne au consommateur code une surface prévisible API tout en vérifiant le contrat réel plutôt qu'un fixture fixe. guide de configuration de l'intégration continue est une référence utile si votre équipe a encore besoin d'une base 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. Le 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 rend le flux honnête.
Pipelines maintenables, Performance et un Dernier Contrôle

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 le générateur et documentez comment les changements brisants sont approuvés. Si le processus est flou, les gens contourneront le système, 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 les travaux de compilateur répétitifs, 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 intelligent.
Le tableau de contrôle ci-dessous est celui qu'il est utile de garder à l'esprit :
- Fixation de version : Bloquez la
openapi-typescriptversion enpackage.jsonAinsi, l'output ne dérive pas d'un ordinateur à l'autre. - Examen de schéma : Traitez les modifications de spécification comme des changements de contrat examinables, et non comme des tâches de maintenance.
- Détection de dérive : Régeneratez en 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.
Une pipeline qui inclut ces portes ne génère pas seulement des types, elle 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 faire passer rapidement les corrections JavaScript, CSS, copie, configuration et d'actifs sans attendre la revue des magasins d'applications. Capgo Pour voir comment ses ensembles signés, la protection de roulement et les contrôles de publication s'intègrent dans un processus de publication qui a besoin de vitesse sans perdre le contrôle.