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 au 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 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 devraient échouer rapidement en temps de build au lieu de se faufiler en temps d'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'un API sécurisé
- Génération de types TypeScript à partir d'une spécification OpenAPI
- Choisir entre des types purs, des clients complets et pas de 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 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 un PR qui ajoute un champ de réponse optionnel. Le fichier généré met à jour proprement, la diff ressemble à du plomberie, et tout le monde passe à autre chose. Puis la 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'embuscade 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 et vers la manière dont les systèmes se connectent.
Où les échecs se cachent
Les points de rupture les plus courants sont ennuyeux, 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 un 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 du 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 la conformité 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 de schéma à 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 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 engagé, et faites visible la dérive dans CI au lieu de compter sur quelqu'un pour se rappeler 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
Le -o La bannière de sortie compte car elle rend l'artefact généré explicite. --immutable Est utile lorsque vous souhaitez que les types générés conservent l'intention readonly dans la sortie, et --alphabetize Conserve les différences stables lorsque l'ordre du schéma change sans signification sémantique. --enum Compte lorsque votre équipe préfère les énumérations 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 typeset non un runtime de client ou un niveau de requête, et cette limitation aide lorsque vous souhaitez 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 mises à jour restent actifs, comme discuté dans le cas de la maintenance open sourceUne lecture pratique de cette posture est le dépôt de projet GitHub repository and CLI documentation.
et la documentation de __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. En 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 , ce 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. anyConservez la spécification explicite, ou le générateur exposera l’ambiguïté à votre retour.
Le workflow qui dure est simple. Placez la spécification sous contrôle de version, régénérez à chaque construction, commitez le fichier généré et laissez le vérificateur de types protester avant que quiconque fusionne un désaccord. Cela vous donne une frontière de contrat stable pour le reste de la chaîne de production.
so
Choisir entre les types purs, les clients complets et sans génération de code
| Modèle | Temps de construction | Fichiers de sortie | Poids de l'ensemble | Meilleure correspondance |
|---|---|---|---|---|
| Pures types avec un enveloppe mince | Rapide | Peu | Bas | Équipes qui veulent le contrôl’et une surface de runtime petite |
| Génération de code client complète | Lent | Beaucoup | Plus élevé | Les équipes qui veulent un transfert rapide et des opérations générées automatiquement |
| Pas de constructeurs de requêtes de génération de code | Rapide | Aucun ou minimal | Faible | 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 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 sortie générée 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 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 rapidité de la remise
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 remise 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 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 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 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 malin. Dans la plupart des configurations de production, ce niveau reste autour de 30–60 lignes car les types générés portent déjà la plupart de la forme.
Ici est 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 de sorte que les filtres optionnels ne se transforment pas en bouillie de chaînes.
- Les corps de réponse restent typés de sorte que le traitement de code peut se fier à la forme étroite qu'il attend.
Un wrapper comme celui-ci est intentionnellement ennuyeux. Il ne doit pas inventer des retentes, des transformations ou des politiques d'autorisation si ceux-ci appartiennent àilleurs. Il doit déplacer la demande d'une opération typée dans la couche de transport et la retourner ensuite sous forme de résultat typé.
Gardez le wrapper ennuyeux et léger en termes de 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è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 floue qui se fait masser plus tard.
Si vous utilisez bien cette jointure, 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 au runtime avec zod, ajv ou io-ts
Les types TypeScript disparaissent au 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 à la frontière 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 gèrent les contrôles de frontière qui ne peuvent pas être compilés par les types de temps d'exécution.
Valider où les données pénètrent
Pour les applications React, la frontière est généralement juste après que la requête est 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 transmis à une règle commerciale. La règle est simple, gardez la validation proche de la frontière et ne dispersiez pas les contrôles manuels à travers les fonctionnalités code.
A 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 le contrôle de runtime aligné sur ce que le générateur a produit. ajv est un choix fort quand 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 meilleur seuil 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 la CI

Une chaîne de pipeline qui dure transforme le contrat en une porte, et non en une suggestion. Regénérer les types, échouer en cas de dérive, exécuter 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 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 depuis le dépôt ou la source générée.
- Re régénérer les types.
- Échouer à la tâche si
git diffmontre des changements. - Exécuter
tsc --noEmit. - Exécuter une 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 sont séparés par le temps ou les limites de l'équipe. Cela donne au consommateur code une surface prévisible API tout en vérifiant le contrat réel plutôt qu'un fixe dur. 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 invisible de sortie. Si un développeur met à jour localement le générateur et que l'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 contrôle

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 génération 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 les travaux de compilateur répétés, 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.
La liste de contrôle ci-dessous est celle qui vaut la peine de la garder à portée de main:
- Fixez la version : Verrouillez
openapi-typescriptversion danspackage.jsonainsi, l'output ne dérive pas d'une machine à l'autre. - Révision du schéma : Considérer les changements de spécification comme des modifications contractuelles susceptibles d'être examinées, et non comme des tâches de maintenance.
- Détecter le dérive : Régenerer en CI et échouer en cas de différence.
- Validation de bord : Analyser les payloads non fiables avant qu'elles ne parviennent à l'état d'application ou à la persistance.
- Test de contrat : Lancer une vérification avec un mock qui prouve que le consommateur code correspond toujours au schéma.
- Politique de changement de rupture : Enregistrer qui approuve les changements de forme et comment les clients sont informés.
Au pipeline qui inclut ces portes, il ne s'agit pas seulement de générer 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 mettre à jour rapidement les correctifs JavaScript, CSS, copie, configuration et actifs sans attendre la revue des magasins d'applications. Visitez Capgo Pour voir comment ses ensembles signés, la protection de rollback et les contrôles de publication s'intègrent dans un processus de publication qui nécessite de la vitesse sans perdre le contrôle.