@hoajs/valibot
Valibot validator middleware for Hoa.
valibotValidator reads values from ctx.req using the keys you define in the schema:
{ query: v.object({...}) }→ctx.req.query.{ headers: v.object({...}) }→ctx.req.headers.{ params: v.object({...}) }→ctx.req.params(requires@hoajs/routerto populateparams).{ body: v.object({...}) }→ctx.req.body(requires@hoajs/bodyparserto populatebody).- URL parts (
href,origin,protocol,host,hostname,port,pathname,search,hash,method) → corresponding fields onctx.req.
On success, the validated value is written back to ctx.req[key]. On failure, it throws 400 with a merged error message (deduplicated and joined by ; by default).
Quick Start
js
import { Hoa } from 'hoa'
import { router } from '@hoajs/router'
import { v, valibotValidator } from '@hoajs/valibot'
const app = new Hoa()
app.extend(router())
app.get(
'/users/:name',
valibotValidator({
params: v.object({
name: v.string()
}),
// query: v.object({...}),
// headers: v.object({...}),
// body: v.object({...}),
// ...
}),
async (ctx) => {
const name = ctx.req.params.name
ctx.res.body = `Hello, ${name}!`
}
)
export default appExamples
- Validate query parameters
js
app.get(
'/search',
valibotValidator({ query: v.object({ key1: v.string(), key2: v.string() }) }),
(ctx) => { ctx.res.body = { valid: ctx.req.query } }
)- Validate headers (preserve undeclared headers)
js
app.use(valibotValidator({
headers: v.looseObject({ 'x-foo': v.literal('bar') })
}))
// ctx.req.headers will keep extra headers, e.g. x-extra- Validate URL parts
js
app.use(valibotValidator({
url: v.instance(URL),
href: v.pipe(v.string(), v.url()),
origin: v.string(),
protocol: v.literal('http:'),
host: v.literal('example.com:8080'),
hostname: v.literal('example.com'),
port: v.literal('8080'),
pathname: v.literal('/users'),
search: v.literal('?id=1'),
hash: v.literal('#frag'),
method: v.literal('GET')
}))Abort options
valibotValidator supports two options to control how validation errors are collected:
abortEarly(boolean):true→ stop collecting after the first issue (per key) and return a single error.false→ collect all issues (per key) and return merged errors.
abortPipeEarly(boolean):- When using
v.pipe(...),true→ stop pipe at the first failing check. false→ collect all failing checks in the pipe.
- When using
js
// Example: abortEarly
app.use(valibotValidator({
body: v.object({ a: v.number(), b: v.number() })
}, { abortEarly: true }))
// Example: abortPipeEarly
const pipeSchema = v.pipe(
v.string(),
v.check(() => false, 'M1'),
v.check(() => false, 'M2')
)
app.use(valibotValidator({ body: v.object({ p: pipeSchema }) }, { abortPipeEarly: true }))Custom error formatting
By default, error messages are de-duplicated and joined by a semicolon. You can customize them:
js
app.use(valibotValidator({
query: v.object({ id: v.number() })
}, {
formatError: (issues, ctx, key, value) => `Wrong ${key}, got ${String(value?.id)}`
}))or use ctx.throw:
js
app.use(valibotValidator({
query: v.object({ id: v.number() })
}, {
formatError: (issues, ctx, key, value) => ctx.throw(412, `Wrong ${key}, got ${String(value?.id)}`)
}))