Formulaires pilotés par schéma dans React : construire avec TrueFoundry FormBuilder

Conçu pour la vitesse : latence d'environ 10 ms, même en cas de charge
Une méthode incroyablement rapide pour créer, suivre et déployer vos modèles !
- Gère plus de 350 RPS sur un seul processeur virtuel, aucun réglage n'est nécessaire
- Prêt pour la production avec un support complet pour les entreprises
Si vous avez déjà utilisé TrueFoundry pour déployer un service, créer un cluster, configurer un modèle LLM ou gérer des secrets, vous avez interagi avec des formulaires qui semblent classiques, mais qui sont conçus très différemment des formulaires web habituels.
Pourquoi TrueFoundry utilise des formulaires basés sur des schémas
Les formulaires TrueFoundry représentent souvent des manifestes, c'est-à-dire des objets YAML/JSON que vous pouvez également appliquer via l'interface de ligne de commande (tfy apply -f ...). Le même objet peut être modifié dans l'interface utilisateur, téléchargé au format YAML ou soumis à l'API.
Une approche basée sur des schémas offre à TrueFoundry :
- Une source unique de vérité pour la structure des formulaires (chargée depuis le backend en fonction du type de ressource).
- Un comportement cohérent pour les déploiements, les clusters, les politiques, les modèles, les secrets, les formulaires de paramètres ou tout autre formulaire de la plateforme.
- Des champs imbriqués et conditionnels sans avoir à réécrire la logique de formulaire sur chaque écran.
- Des widgets spécifiques au domaine (sélecteur de cluster, sélecteur de secret, limites de ressources) intégrés à un runtime partagé.
Le modèle mental : le schéma décrit la forme des données et la manière de les modifier ; FormBuilder transforme cette description en un formulaire fonctionnel.
1. Schéma de formulaire :
Un schéma de formulaire est un tableau de définitions de champs. Chaque champ est un objet possédant quelques propriétés importantes :
Voici un exemple générique simplifié ; il ne s'agit pas d'un véritable schéma TrueFoundry, mais il illustre la manière dont le produit conçoit la configuration :
[
{
"sort": 1,
"jsonKey": "name",
"label": "Service Name",
"uiType": "Input",
"validate": { "required": true, "pattern": "^[a-z0-9-]+$" }
},
{
"sort": 2,
"jsonKey": "image",
"label": "Image",
"uiType": "Group",
"subParameters": [
{
"jsonKey": "type",
"label": "Source",
"uiType": "Radio",
"validate": {
"required": true,
"defaultValue": "build",
"options": [
{ "label": "Build", "value": "build" },
{ "label": "Existing image", "value": "existing" }
]
}
},
{
"jsonKey": "uri",
"label": "Image URI",
"uiType": "Input",
"conditions": [
{ "jsonKey": "image.type", "op": "==", "value": "existing" }
],
"validate": { "required": true }
}
]
}
]
Manifeste/configuration résultant lors de la soumission :
{
"name": "my-service",
"image": { "type": "existing", "uri": "registry.io/app:v1" }
}
2. Comment les composants sont mappés et les champs rendus
Le rendu s'effectue via un petit pipeline, selon le flux suivant :

FormBuilder parcourt l'arborescence du schéma et sélectionne un composant React par nœud en fonction du uiType.
Types de composants:
- Basique : Input, Select, Radio, Switch, Number
- Structurel : Group (section), Structs (liste répétable), KV / ENV (clé-valeur)
- Spécifique au domaine : ClusterSelect, SecretSelect, Resources, ModelSelect, PermissionsMatrix, champs MCP, et plus encore
Conditions: Si image.type !== "existing", le champ URI n'est jamais monté. Avec shouldUnregister: true, il est également supprimé de l'état du formulaire, empêchant ainsi les valeurs masquées de s'infiltrer dans la charge utile.
Logique de mappage dans le code :
// FormComponentMap
const ComponentType = FormComponents[schema.uiType]
const CustomComp = CustomComponentsMap?.[schema.uiType]
if (!enabledByCondition) return null
return CustomComp
? <CustomComp schema={schema} />
: <ComponentType schema={schema} />
3. Gestion de l'état du formulaire
FormBuilder utilise react-hook-form comme moteur d'état. Ce choix est déterminant pour le comportement du produit.
Initialisation/Utilisation des composants - création et modification :
<FormBuilder
schema={schema}
defaultValues={existingManifest} // edit: pre-fill; create: empty/template
onSubmit={(manifest) => createOrUpdate({ manifest })}
/>
Configuration du formulaire :
const methods = useForm({
mode: 'onChange', // validate as user types
defaultValues,
shouldUnregister: true, // hidden fields drop out of state
})
Enregistrement des champs - chaque entrée est liée à son chemin :
// Inside a text field component
const { register } = useFormContext()
<input
{...register(schema.jsonKey, registerProps)}
defaultValue={defaultValue}
/>
Contexte supplémentaire - le formulaire spécifique transmet des données d'exécution distinctes des valeurs des champs :
<FormBuilder
schema={schema}
extraContext={{
workspace,
cluster,
serviceAccountOptions, // dynamic dropdown options
dataTestPrefix: 'create-cluster',
}}
/>
Objet unique, chemins imbriqués :
Toutes les valeurs des champs résident dans un objet de formulaire unique. Chaque champ s'enregistre sous son chemin jsonKey complet :
- name → chaîne de caractères de premier niveau
- image.type → valeur imbriquée
- ports.0.container_port → premier élément d'un tableau
Lorsque vous modifiez un champ, vous modifiez cet objet partagé. Lors de la soumission, FormBuilder lit l'objet complet et le transmet au gestionnaire du formulaire (qui appelle généralement l'API TrueFoundry ou génère du YAML).
Valeurs par défaut et mode édition :
Lors de l'ouverture d'un formulaire pour créer un élément, les defaultValues peuvent être vides ou provenir d'un modèle.
Lors de la modification, defaultValues correspond généralement au manifeste existant. Les champs du formulaire sont pré-remplis à partir de cet objet. Certains champs sont marqués comme immuables en mode édition (par exemple, le nom de la ressource) et s'affichent donc en lecture seule.
Les champs masqués sont supprimés de l'état :
Les formulaires TrueFoundry utilisent shouldUnregister: true. Cela signifie que :
- Si un champ est masqué par une condition, il est désenregistré de l'état du formulaire
- Sa valeur n'est pas incluse dans la charge utile soumise
Ceci est important pour les formulaires conditionnels : vous ne soumettez que ce que l'utilisateur a pu réellement voir et modifier.
4. Fonctionnement de la validation
La validation dans les formulaires TrueFoundry s'effectue à deux niveaux.
Niveau 1 : Validation du schéma (déclarative)
Le bloc validate de chaque champ peut spécifier :
- required
- min / max (plage autorisée pour une valeur entière)
- minLength / maxLength (longueur d'une chaîne ou d'un tableau)
- pattern (regex) avec message personnalisé
- defaultValue
- immutable (lecture seule en mode édition)
Ces règles sont converties en règles de validation react-hook-form. Les erreurs s'affichent en ligne à côté du champ. La validation s'exécute lors du changement (mode : 'onChange'), afin que les utilisateurs reçoivent un retour au fur et à mesure de leur saisie, et non uniquement lors de la soumission.
Niveau 2 : Validation personnalisée et asynchrone
Les schémas peuvent également inclure une fonction de validation personnalisée, couramment utilisée pour :
- L'unicité du nom (« Une entité portant ce nom existe déjà »)
- Vérifications via API avant soumission
- Règles inter-champs au sein d'un composant personnalisé
- Les écrans les associent au moment de l'exécution grâce à des fonctions d'assistance telles que « attacher la validation au nom du champ ». L'interface affiche un indicateur de chargement pendant que la validation asynchrone s'exécute (par exemple, une vérification de nom avec debounce).
useAttachValidation(schema, {
name: async (name) => {
const taken = await checkNameExists(name)
return taken ? 'Name already exists' : true
},
})En résumé : l'expérience utilisateur
Lorsque vous ouvrez « Créer un cluster », « Déployer un service », « Ajouter un modèle » ou « Gérer les secrets » :
- TrueFoundry charge (ou génère) un schéma pour cette ressource
- FormBuilder affiche chaque nœud du schéma sous forme de composant de saisie approprié
- Vos modifications mettent à jour un objet de formulaire imbriqué unique
- La validation s'exécute en continu, puis à nouveau lors de la soumission
- L'objet final est un manifeste, présentant la même structure que celle utilisée dans un fichier YAML ou via l'interface en ligne de commande (CLI)
C'est pourquoi TrueFoundry peut prendre en charge des interfaces de configuration très complexes sans que chaque écran ne soit un formulaire spécifique : la complexité réside dans le schéma et dans une bibliothèque de composants de champs, orchestrés par un moteur d'exécution partagé.
TrueFoundry AI Gateway offre une latence d'environ 3 à 4 ms, gère plus de 350 RPS sur 1 processeur virtuel, évolue horizontalement facilement et est prête pour la production, tandis que LiteLM souffre d'une latence élevée, peine à dépasser un RPS modéré, ne dispose pas d'une mise à l'échelle intégrée et convient parfaitement aux charges de travail légères ou aux prototypes.



Gouvernez, déployez et suivez l'IA dans votre propre infrastructure
Blogs récents
Questions fréquemment posées
Pourquoi choisir une approche pilotée par un schéma plutôt que de coder les formulaires manuellement ?
Les formulaires pilotés par un schéma constituent une source unique de vérité pour vos manifestes de configuration. Ils garantissent un comportement cohérent de l'interface utilisateur, prennent en charge la logique imbriquée/conditionnelle sans duplication de code, et permettent au backend de piloter la structure du formulaire de manière dynamique.
Comment fonctionne la validation dans FormBuilder ?
La validation s'effectue sur deux niveaux : la validation de schéma déclarative (utilisant des règles comme obligatoire, motif ou min/max) et la validation personnalisée/asynchrone pour les contrôles adossés à une API, tels que la vérification de noms de ressources uniques.
Puis-je l'intégrer avec des bibliothèques React existantes ?
Oui. Le FormBuilder de TrueFoundry utilise react-hook-form comme moteur d'état principal, ce qui le rend compatible avec les modèles React standards et facile à étendre avec des composants personnalisés.










.webp)



.png)
.png)
.png)
.png)
.png)






.png)







