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
or form
, the request must contain a matching content-type
header (e.g. Content-Type: application/json
for json
). Otherwise, the request body will not be parsed and you will receive an empty object ({}
) as value in the callback.
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) // {}
// ✅ 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 new 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 new 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 * as 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
}
)
Standard Schema Validator Middleware
Standard Schema is a specification that provides a common interface for TypeScript validation libraries. It was created by the maintainers of Zod, Valibot, and ArkType to allow ecosystem tools to work with any validation library without needing custom adapters.
The Standard Schema Validator Middleware lets you use any Standard Schema-compatible validation library with Hono, giving you the flexibility to choose your preferred validator while maintaining consistent type safety.
npm i @hono/standard-validator
yarn add @hono/standard-validator
pnpm add @hono/standard-validator
bun add @hono/standard-validator
Import sValidator
from the package:
import { sValidator } from '@hono/standard-validator'
With Zod
You can use Zod with the Standard Schema validator:
npm i zod
yarn add zod
pnpm add zod
bun add zod
import { z } from 'zod'
import { sValidator } from '@hono/standard-validator'
const schema = z.object({
name: z.string(),
age: z.number(),
})
app.post('/author', sValidator('json', schema), (c) => {
const data = c.req.valid('json')
return c.json({
success: true,
message: `${data.name} is ${data.age}`,
})
})
With Valibot
Valibot is a lightweight alternative to Zod with a modular design:
npm i valibot
yarn add valibot
pnpm add valibot
bun add valibot
import * as v from 'valibot'
import { sValidator } from '@hono/standard-validator'
const schema = v.object({
name: v.string(),
age: v.number(),
})
app.post('/author', sValidator('json', schema), (c) => {
const data = c.req.valid('json')
return c.json({
success: true,
message: `${data.name} is ${data.age}`,
})
})
With ArkType
ArkType offers TypeScript-native syntax for runtime validation:
npm i arktype
yarn add arktype
pnpm add arktype
bun add arktype
import { type } from 'arktype'
import { sValidator } from '@hono/standard-validator'
const schema = type({
name: 'string',
age: 'number',
})
app.post('/author', sValidator('json', schema), (c) => {
const data = c.req.valid('json')
return c.json({
success: true,
message: `${data.name} is ${data.age}`,
})
})