Validation
@bext-stack/framework/validation valide une action serveur FormData, un corps de requête JSON ou tout objet simple par rapport à un schéma que vous écrivez dans TypeScript, et vous renvoie une valeur saisie et forcée ou une liste d'erreurs par champ. Il s'agit de l'équivalent pour les développeurs d'applications de la fonctionnalité Rust Validateur de demande : la fonctionnalité valide les fichiers de configuration et les charges utiles de la file d'attente côté serveur ; ce module est ce que vous recherchez à l'intérieur d'un PRISM action.
Il est en forme de zod donc le modèle mental transfère — v.object({...}),
.parse / .safeParse - mais il s'agit de quelques centaines de lignes de TypeScript pur avec zéro dépendance, et il comprend le monde entièrement composé de chaînes des formulaires HTML (les chaînes numériques deviennent des nombres, les cases à cocher deviennent des booléens, les champs répétés deviennent des tableaux).
Quand l'utiliser#
- Actions du serveur et routes API — validez
requestavant de lui faire confiance. - Formulaires multi-erreurs — affichez le problème de chaque champ en même temps, pas seulement le d'abord, tout en conservant les valeurs saisies par l'utilisateur lors d'un nouveau rendu.
- Entrée typée — transforme
FormData(toutes les chaînes) en un objet typé avec nombres, booléens et énumérations, vérifiés à la limite.
Pour une seule vérification de type ad hoc, un if en ligne est plus simple. Atteignez-le lorsque la forme est réelle, partagée ou montrée à un utilisateur.
L’ordre des règles est significatif. errorsByField renvoie le premier message par champ, donc déclarer .nonempty("Required") avant .email("Malformed") vous donne gratuitement l'UX naturelle du « format ALORS requis » - tandis que errorsByFieldAll expose toujours chaque échec de vérification.
Générateurs de schémas#
Atteignez tous les constructeurs via l'espace de noms v.
| Constructeur | Produit | Méthodes notables |
|---|---|---|
v.string(opts?) |
string |
.trim() .toLowerCase() .nonempty() .min(n) .max(n) .length(n) .regex(re) .email() .url() |
v.number(opts?) |
number (contraint à partir de chaînes numériques) |
.int() .min(n) .max(n) .positive() .nonnegative() |
v.boolean() |
boolean |
sémantique de la case à cocher — absent → false, "on" / "true" / "1" → true, jamais d'erreurs |
v.enum([...] as const) |
l'union des littéraux | — |
v.literal(x) |
la valeur exacte x |
— |
v.array(inner) |
T[] |
.min(n) .max(n) .nonempty() — un seul champ de formulaire répété compte comme une liste à 1 élément |
v.object(shape) |
le type d'objet déduit | .partial() (tous en option) .strict() (rejeter les clés inconnues) |
Chaque constructeur peut être chaîné avec .optional(), .nullable(),
.default(value), .refine(test, message) et .transform(fn). Le
opts sur v.string / v.number remplace les messages intégrés requis et de type incorrect (ceux que les méthodes de contrainte ne peuvent pas atteindre) - c'est le crochet pour la copie localisée.
Valider une demande#
validate(input, schema) lit le corps pour vous — FormData,
application/json, ou x-www-form-urlencoded — valide ensuite :
import { v, validate, errorsByField, type Infer } from "@bext-stack/framework/validation";
const Signup = v.object({
email: v.string().trim().nonempty("Email required").email("Looks malformed"),
username: v.string().trim().min(3).regex(/^[a-z0-9_]+$/i, "letters, digits, _ only"),
age: v.number().int().min(13).max(120),
});
type Signup = Infer<typeof Signup>; // { email: string; username: string; age: number }
export async function action({ request }: { request: Request }) {
const result = await validate(request, Signup);
if (!result.ok) {
// Return a plain object → PRISM re-renders the page with actionData,
// so the form shows per-field errors and keeps the user's typing.
return { ok: false, errors: errorsByField(result.errors) };
}
const user = result.data; // fully typed & coerced — user.age is a number
await createUser(user);
return new Response(null, { status: 303, headers: { Location: "/welcome" } });
}
Vous préférez éviter le passe-partout ? validated(schema, handler) encapsule une action afin que votre gestionnaire ne s'exécute que sur une entrée valide et reçoive les données saisies ; courts-circuits d'entrée non valides vers une charge utile de re-rendu { ok: false, errors, values } :
export const action = validated(Signup, async (data, { request }) => {
await createUser(data); // data is Signup, typed & coerced
return new Response(null, { status: 303, headers: { Location: "/welcome" } });
});
Règles de coercition#
Étant donné que les formulaires HTML envoient tout sous forme de chaînes, les constructeurs contraignent plutôt que rejettent :
v.number()analyse une chaîne numérique ("27"→27) ; un champ vide ou absent estrequired, une chaîne non numérique estinvalid.v.boolean()suit la sémantique des cases à cocher : une case à cocher absente estfalse, une celui actuel ("on") esttrue. Il n'y a jamais d'erreurs.v.array(inner)traite une valeur unique comme une liste à un élément, donc une valeur répétée le champ de formulaire (tag=a&tag=b) et un seultag=asont tous deux validés.- Rapport
v.object/v.arrayimbriqué chemins en pointillés / entre crochets —address.zip,tags[2]— donc l'erreur correspond directement à vos entrées.
Aides aux erreurs#
| Auxiliaire | Retours | Utiliser pour |
|---|---|---|
errorsByField(errors) |
{ field: firstMessage } |
le formulaire commun à un message par entrée |
errorsByFieldAll(errors) |
{ field: string[] } |
montrant tous les problèmes sur un terrain |
stringValues(raw) |
{ field: string } |
repeupler un formulaire rejeté |
formToObject(fd) |
objet simple | quand on veut l'objet brut avant de valider |
.parse(input) lance un ValidationError (portant .errors ) en cas d'échec ;
.safeParse(input) ne lance jamais et ne renvoie jamais un ValidationResult.
Relation avec la fonctionnalité Request Validator#
Le Rust Validateur de demande est un trait de plugin côté serveur permettant de valider les fichiers de configuration, les charges utiles de webhook décodées et les messages de file d'attente avant qu'ils n'atteignent un gestionnaire - soutenu par JSON-Schema / CUE, sélectionné par
BEXT_VALIDATOR_PROVIDER. Ce module est le chemin isolé TypeScript pour la validation de demande/formulaire dans le code de votre application : aucun fournisseur à configurer, aucun aller-retour d'hôte et le schéma réside à côté du code qui l'utilise. Utilisez la fonctionnalité aux limites du plugin/configuration ; utilisez-le dans vos itinéraires PRISM.
Essayez-le#
Une démo exécutable - un formulaire d'inscription qui signale chaque erreur de champ en un seul passage et 303 s en cas de succès - se trouve sur démo. bext.dev/examples/validation, source dans sites/demo/src/app/examples/validation/page.tsx.
Voir aussi#
- Validateur de demande — la capacité Rust que cela complète.
- Actions du serveur — où la validation s'exécute.
- Données PRISM (chargeur / action) — le flux de rendu
actionData. - Boîte à outils de candidature — le reste de la couche d'application TypeScript.