Aller au contenu

Validation

defineHandler enveloppe un handler de route pour ajouter la validation de schéma et l’inférence de types. Il accepte n’importe quelle bibliothèque qui implémente la spec Standard Schema — Zod, Valibot et ArkType fonctionnent tous nativement.

import { defineHandler } from 'lacis'
import { z } from 'zod'
export const GET = defineHandler({
params: z.object({ id: z.string().uuid() }),
query: z.object({ verbose: z.coerce.boolean().optional() }),
meta: { summary: 'Obtenir un utilisateur par ID', tags: ['users'] },
handler: async (req, res) => {
req.params.id // string — validé et typé
req.query.verbose // boolean | undefined — validé et typé
res.json({ id: req.params.id })
},
})

La valeur retournée est une fonction async classique — exportez-la comme tout autre handler de route.

Chaque champ accepte un Standard Schema. Les schémas sont validés dans l’ordre : params en premier, puis query, puis body. Si l’un échoue, Lacis retourne 400 immédiatement.

  • params — validé contre req.params (paramètres de chemin URL)
  • query — validé contre req.query (query string parsée)
  • body — Lacis appelle req.json() en interne et valide le résultat

Attaché à l’opération OpenAPI lors de la génération du spec. Sans effet au moment de la requête.

meta: {
summary: 'Créer un utilisateur',
description: 'Crée un nouveau compte utilisateur.',
tags: ['users'],
deprecated: false,
}

Le handler de requête avec req et res entièrement typés.

Quand la validation échoue, Lacis envoie un 400 automatiquement :

{
"error": "Validation failed",
"issues": [
{ "message": "Invalid uuid", "path": ["id"] }
]
}

Le handler n’est jamais appelé en cas d’échec de validation.

Les query strings sont toujours des chaînes. Utilisez les utilitaires de coercition de votre validateur :

query: z.object({
page: z.coerce.number().int().positive(),
active: z.coerce.boolean().optional(),
})
import { defineHandler } from 'lacis'
import { z } from 'zod'
export const POST = defineHandler({
body: z.object({
name: z.string().min(1),
email: z.string().email(),
}),
meta: { summary: 'Créer un utilisateur', tags: ['users'] },
handler: async (req, res) => {
const { name, email } = req.body // entièrement typé
res.status(201).json({ name, email })
},
})
export const PUT = defineHandler({
params: z.object({ id: z.string().uuid() }),
query: z.object({ dryRun: z.coerce.boolean().default(false) }),
body: z.object({ name: z.string(), email: z.string().email() }),
handler: async (req, res) => {
if (req.query.dryRun) return res.json({ ok: true, dry: true })
res.json({ id: req.params.id, ...req.body })
},
})

TypeScript infère les types de sortie de vos schémas. Modifiez un schéma et les types dans handler se mettent à jour automatiquement — aucune annotation manuelle nécessaire.