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 vous avez encadré OpenAPI TypeScript en tant que 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'un API sécurisé
- La 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 de exécution avec zod, ajv ou io-ts
- Mettre en place les 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'une API sécurisée
Un collègue fusionne un PR qui ajoute un champ de réponse optionnel. Le fichier généré met à jour proprement, la diff est ennuyeuse, et tout le monde passe à autre chose. Puis la frontière continue de lire une forme plus ancienne à travers un wrapper manuscrit qui a été 'temporairement' cast avec as any, et la production commence à se comporter comme si le contrat n'avait jamais changé.
C'est là l'embuscade avec les types générés. TypeScript ne peut protéger que le code qui consomme les types générés, et seulement si le niveau 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 de connexion est utile ici car elle pousse la conversation loin d'une seule outil 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. Le dérive du schéma s'produit lorsque la spécification OpenAPI et le service déployé cesseront de correspondre. La couverture partielle s'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 une cast de réponse lâche.
Règle pratique : si l'enveloppe peut mentir, le générateur ne peut pas vous sauver.
Existe également un écart de temps d'exécution. Les types TypeScript disparaissent après compilation, ils ne peuvent donc pas rejeter un JSON mal formé en provenance de la liaison. 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 le cycle de vie d'une 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 à typescript openapi est celle-ci. Cela vous donne un pont strict entre schéma et types, ce qui est excellent, mais il ne valide pas les requêtes, n'impose pas la forme du payload en temps d'exécution, ou ne stoppe pas un wrapper maladroit qui remet tout en question. Le générateur est le 20 % facile. Le reste est la conception du pipeline, et c'est là que les équipes gagnent la 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 à des changements réels dans le dépôt. Gardez la spécification OpenAPI dans le même dépôt, 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 mise à jour. 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 l'output flag compte car il rend explicite l'artefact généré. --immutable est utile lorsque vous souhaitez que les types générés conservent l'intention readonly dans l'output, et --alphabetize garde les diffs stables lorsque l'ordre du schéma change sans signification sémantique. --enum compte lorsque votre équipe préfère les enums dans la surface générée au lieu des unions.
The documentation du projet est claire sur le champ, il s'agit d'un générateur de typeset non d'un runtime de client ou d'une couche de requête, et cette limitation aide lorsque vous souhaitez 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 partie de la raison pour laquelle les outils open source peuvent tenir dans la production lorsque les documents et les releases restent actifs, comme discuté dans le cas de la maintenance open source. Une lecture pratique de cette posture est le dépôt du projet GitHub repository and CLI documentation.
et la documentation de __CAPGO_KEEP_1__ sur la génération de Wire dans package.json alors que le commandement vit côte à côte avec les autres scripts de construction, puis exécutez-le chaque fois que la spécification change. Dans CI, régénérer le fichier et échouez si git diff montre un dérive. Cela transforme les modifications de contrat en travail de revue visible au lieu de risque silencieux en temps d'exécution.
Le côté 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 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 la spécification manquante sont mis en surface plus tôt au lieu d'être cachés sous des types permissifs. anyConservez la spécification explicite, ou le générateur exposera fidèlement l'ambiguïté à votre intention.
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 protester avant que quiconque fusionne une incohérence. Cela vous donne une frontière de contrat stable pour le reste de la chaîne de pipeline.
alors que le commandement vit côte à côte avec les autres scripts de construction, puis exécutez-le chaque fois que la spécification change. Dans CI, régénérer le fichier et échouez si
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 mince | Rapide | Peu | Faible | Les équipes qui veulent le contrôl’et une surface de runtime petite |
| Génération de code client complète | Plus lent | Beaucoup | Plus élevé | Les équipes qui veulent un transfert rapide et des opérations générées automatiquement |
| Aucun constructeur de requêtes de génération de code | Rapide | Ni l'un ni l'autre ou minimal | Bas | Les applications monocodage qui préfèrent la logique de transport écrite à la main |
La choix n'est vraiment pas « lequel outil gagne ». C'est quelle forme de pipeline convient à votre dépôt, à votre équipe et à combien de changements l’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 produit d'output en environ 1,5 secondes en moyenne, par rapport à 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
Un ensemble 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 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 possède les deux extrémités de la forme et que les modifications de API sont étroitement coordonnées. Le revers est la discipline de maintenance. Plus les équipes et les dépôtoires sont entre producteur et consommateur, plus il est probable que la couche de requête manuscrite dérive, à moins que vous 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 la sortie, 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 le bon échange interne.
Brancher un Client Épais 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, cette couche reste autour de 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 donc
/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 ainsi que la mise en forme de code peut se fier à la forme étroite qu'elle attend.
Un wrapper comme celui-ci est intentionnellement ennuyeux. Il ne doit pas inventer des retentis, 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 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 vouliez que le générateur révèle.
Pour les équipes qui préfèrent Axios, le modèl’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 flou qui se fait masser plus tard.
Si vous utilisez bien cette articulation, openapi typescript fournit une division claire du travail. Le schéma vit dans la spécification, le transport dans l' enveloppe, et l'application voit des opérations typées au lieu de requêtes ad hoc code.
Ajouter une validation en temps réel avec zod, ajv ou io-ts
Les types TypeScript disparaissent à l'exécution, et le réseau n'a pas d'intérêt pour 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, ajv, et io-ts gèrent les contrôles de limite qui ne peuvent pas être compilés en temps 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 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 transmis à une règle commerciale. La règle est simple, gardez la validation proche de la limite et ne dispersiez pas les contrôles manuels à travers les fonctionnalités code.
A zod une 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 réel alignée sur ce que le générateur a produit. ajv est un choix fort lorsque vous voulez une validation de schéma JSON à haute performance sur le serveur, tandis io-ts encore convient aux équipes qui vivent déjà dans le fp-ts 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 se marie 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 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 beaucoup plus sûr que de se fier à 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

Une chaîne de traitement qui dure transforme le contrat en une porte, et non en une suggestion. Regénérez les types, échouez sur le dérive, exécutez tsc --noEmit, et exercez l’API forme contre un mock ou un outil de contrat 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 workflow pratique ressemble à ceci :
- Extraire la spécification 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éclenner
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 d'application. 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 est séparé par le temps ou les limites de l'équipe. Il donne à code un surface prévisible API tout en vérifiant le contrat réel plutôt qu'un fixture fixe. Le guide de configuration 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.
Pinssez la version du générateur pour éviter l'un des pires échecs dans les pipelines de génération de code, la dérive de sortie invisible. Si un développeur met à jour localement le générateur 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 changements 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.
Maintenabilité des pipelines, performance et un dernier checklist

Les pipelines qui survivent sont ceux avec une gouvernance banale. Versionnez la spécification, passez en revue les changements de schéma comme code, fixez la version du générateur et documentez comment les changements de rupture 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 pour garder 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 checklist ci-dessous est celui qu'il est utile de garder à portée de main :
- Version pinning : Fixez la
openapi-typescriptversion danspackage.jsonde sorte que l'output ne dérive pas d'une machine à l'autre. - Examen de schéma : Traitez les changements de spécification comme des modifications de contrat examinables, et non comme des tâches de routine.
- Détecter le dérive : Régeneratez en CI et échouez sur la différence.
- 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.
Avec une chaîne de pipelines qui inclut ces portes, on ne génère pas seulement des types, on 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 d'actualisation se comporte avec la même discipline, Capgo vous donne un moyen pratique de mettre à jour rapidement les correctifs JavaScript, CSS, copie, configuration et actifs sans attendre la revue des magasins d'applications. Visitez Capgo Écrit par