Validation
Hono provides only a very thin Validator. But, it can be powerful when combined with a third-party Validator. In addition, the RPC feature allows you to share API specifications with your clients through types.
Manual validator
First, introduce a way to validate incoming values without using the third-party Validator.
Import validator
from hono/validator
.
import { validator } from 'hono/validator'
To validate form data, specify form
as the first argument and a callback as the second argument. In the callback, validates the value and return the validated values at the end. The validator
can be used as middleware.
app.post(
'/posts',
validator('form', (value, c) => {
const body = value['body']
if (!body || typeof body !== 'string') {
return c.text('Invalid!', 400)
}
return {
body: body,
}
}),
//...
Within the handler you can get the validated value with c.req.valid('form')
.
, (c) => {
const { body } = c.req.valid('form')
// ... do something
return c.json(
{
message: 'Created!',
},
201
)
}
Validation targets include json
, query
, header
, param
and cookie
in addition to form
.
WARNING
When you validate json
, the request must contain a Content-Type: application/json
header otherwise the request body will not be parsed and you will receive a warning.
It is important to set the content-type
header when testing using app.request()
.
Given an application like this.
const app = new Hono()
app.post(
'/testing',
validator('json', (value, c) => {
// pass-through validator
return value
}),
(c) => {
const body = c.req.valid('json')
return c.json(body)
}
)
Your tests can be written like this.
// ❌ this will not work
const res = await app.request('/testing', {
method: 'POST',
body: JSON.stringify({ key: 'value' }),
})
const data = await res.json()
console.log(data) // undefined
// ✅ this will work
const res = await app.request('/testing', {
method: 'POST',
body: JSON.stringify({ key: 'value' }),
headers: new Headers({ 'Content-Type': 'application/json' }),
})
const data = await res.json()
console.log(data) // { key: 'value' }
WARNING
When you validate header
, you need to use lowercase name as the key.
If you want to validate the Idempotency-Key
header, you need to use idempotency-key
as the key.
// ❌ this will not work
app.post(
'/api',
validator('header', (value, c) => {
// idempotencyKey is always undefined
// so this middleware always return 400 as not expected
const idempotencyKey = value['Idempotency-Key']
if (idempotencyKey == undefined || idempotencyKey === '') {
throw HTTPException(400, {
message: 'Idempotency-Key is required',
})
}
return { idempotencyKey }
}),
(c) => {
const { idempotencyKey } = c.req.valid('header')
// ...
}
)
// ✅ this will work
app.post(
'/api',
validator('header', (value, c) => {
// can retrieve the value of the header as expected
const idempotencyKey = value['idempotency-key']
if (idempotencyKey == undefined || idempotencyKey === '') {
throw HTTPException(400, {
message: 'Idempotency-Key is required',
})
}
return { idempotencyKey }
}),
(c) => {
const { idempotencyKey } = c.req.valid('header')
// ...
}
)
Multiple validators
You can also include multiple validators to validate different parts of request:
app.post(
'/posts/:id',
validator('param', ...),
validator('query', ...),
validator('json', ...),
(c) => {
//...
}
With Zod
You can use Zod, one of third-party validators. We recommend using a third-party validator.
Install from the Npm registry.
npm i zod
yarn add zod
pnpm add zod
bun add zod
Import z
from zod
.
import { z } from 'zod'
Write your schema.
const schema = z.object({
body: z.string(),
})
You can use the schema in the callback function for validation and return the validated value.
const route = app.post(
'/posts',
validator('form', (value, c) => {
const parsed = schema.safeParse(value)
if (!parsed.success) {
return c.text('Invalid!', 401)
}
return parsed.data
}),
(c) => {
const { body } = c.req.valid('form')
// ... do something
return c.json(
{
message: 'Created!',
},
201
)
}
)
Zod Validator Middleware
You can use the Zod Validator Middleware to make it even easier.
npm i @hono/zod-validator
yarn add @hono/zod-validator
pnpm add @hono/zod-validator
bun add @hono/zod-validator
And import zValidator
.
import { zValidator } from '@hono/zod-validator'
And write as follows.
const route = app.post(
'/posts',
zValidator(
'form',
z.object({
body: z.string(),
})
),
(c) => {
const validated = c.req.valid('form')
// ... use your validated data
}
)