Skip to main content

Validation

Model-written

contextclaimmethodsetupresultlimitation

Validation

Hono's thin Validator becomes powerful with third-party validators

Hono provides only a very thin Validator. However, 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.Hono provides only a very thin Validator. However, it can be powerful when combined with a third-party Validator.Hono's Validator is thin but strong with third-party validators RPC also shares API types with clientsHono's Validator is thin but strong with third-party validatorsHono's Validator

Manual validator

The validator middleware checks request parts without third-party libraries

First, introduce a way to validate incoming values without using the third-party Validator.First, introduce a way to validate incoming values without using the third-party Validator.Manual validation works without a third-party ValidatorManual validation works without a third-party ValidatorManual validation

Import validator from hono/validator.Import validator from hono/validator.Import validator from hono/validatorImport validator from hono/validatorvalidator import

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.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.validator takes a target and callback, returning validated values It runs as middleware, so handlers receive only validated datavalidator takes a target and callback, returning validated valuesvalidator callback

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').Within the handler, you can get the validated value with c.req.valid('form').Read validated values with c.req.valid('form')Read validated values with c.req.valid('form')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.Validation targets include json, query, header, param and cookie in addition to form.Targets include json, query, header, param and cookieTargets include json, query, header, param and cookievalidation targets

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.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.json and form validation need a matching content-type header Without it the body is not parsed and the callback receives an empty objectjson and form validation need a matching content-type headercontent-type header

It is important to set the content-type header when testing using app.request().It is important to set the content-type header when testing using app.request().Set content-type when testing with app.request()Set content-type when testing with app.request()app.request()

Given an application like this.Given an application like this.A sample app uses a pass-through json validatorA sample app uses a pass-through json validatorpass-through validator

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.Your tests can be written like this.Omitting the header returns an empty object in tests Adding Content-Type: application/json makes the test return the body valueOmitting the header returns an empty object in testsempty object

// ❌ 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.When you validate header, you need to use lowercase name as the key.header validation keys must be lowercaseheader validation keys must be lowercaselowercase keys

If you want to validate the Idempotency-Key header, you need to use idempotency-key as the key.If you want to validate the Idempotency-Key header, you need to use idempotency-key as the key.Use idempotency-key, not Idempotency-Key, as the header key Mismatched case leaves the value undefined so the middleware always returns 400Use idempotency-key, not Idempotency-Key, as the header keyidempotency-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

Multiple validators can each check a different request part

You can also include multiple validators to validate different parts of request:You can also include multiple validators to validate different parts of request:Chain multiple validators, one per request partChain multiple validators, one per request partMultiple validators

app.post(
  '/posts/:id',
  validator('param', ...),
  validator('query', ...),
  validator('json', ...),
  (c) => {
    //...
  }
)

With Zod

Zod schemas validate manually inside the validator callback

You can use Zod, one of third-party validators. We recommend using a third-party validator.You can use Zod, one of third-party validators. We recommend using a third-party validator.Zod is a recommended third-party validator It is one of several external validators that strengthen Hono's thin oneZod is a recommended third-party validatorZod

Install from the Npm registry.Install from the Npm registry.Install zod from the Npm registryInstall zod from the Npm registryzod install

npm i zod
yarn add zod
pnpm add zod
bun add zod

Import z from zod.Import z from zod.Import z from zodImport z from zodz import

import * as z from 'zod'

Write your schema.Write your schema.Define a schema with z.objectDefine a schema with z.objectz.object

const schema = z.object({
  body: z.string(),
})

You can use the schema in the callback function for validation and return the validated value.You can use the schema in the callback function for validation and return the validated value.Use schema.safeParse in the callback and return parsed.data Failed parses return a 401 text response instead of dataUse schema.safeParse in the callback and return parsed.datasafeParse

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

zValidator wraps a Zod schema into ready middleware

You can use the Zod Validator Middleware to make it even easier.You can use the Zod Validator Middleware to make it even easier.Zod Validator Middleware makes Zod validation easierZod Validator Middleware makes Zod validation easierZod Validator Middleware

npm i @hono/zod-validator
yarn add @hono/zod-validator
pnpm add @hono/zod-validator
bun add @hono/zod-validator

And import zValidator.And import zValidator.Import zValidator from @hono/zod-validatorImport zValidator from @hono/zod-validatorzValidator import

import { zValidator } from '@hono/zod-validator'

And write as follows.And write as follows.zValidator takes the target and a Zod schema The handler then reads typed data via c.req.validzValidator takes the target and a Zod schemazValidator usage

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

sValidator accepts any Standard Schema library with consistent types

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.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.Standard Schema is a common interface for TS validation libraries Zod, Valibot and ArkType maintainers created it so tools need no custom adaptersStandard Schema is a common interface for TS validation librariesStandard Schema

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.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.sValidator supports any Standard Schema library with consistent type safety You choose your preferred validator while Hono keeps type safetysValidator supports any Standard Schema library with consistent type safetysValidator

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 the package:Import sValidator from @hono/standard-validatorImport sValidator from @hono/standard-validatorsValidator import

import { sValidator } from '@hono/standard-validator'

With Zod

Zod works with sValidator through Standard Schema

You can use Zod with the Standard Schema validator:You can use Zod with the Standard Schema validator:Zod schemas pass directly to sValidator The handler reads the validated json through c.req.validZod schemas pass directly to sValidatorZod schema

npm i zod
yarn add zod
pnpm add zod
bun add zod
import * as 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, a lightweight Zod alternative, works with sValidator

Valibot is a lightweight alternative to Zod with a modular design:Valibot is a lightweight alternative to Zod with a modular design:Valibot schemas work with sValidator the same wayValibot schemas work with sValidator the same wayValibot schema

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's TypeScript-native syntax works with sValidator

ArkType offers TypeScript-native syntax for runtime validation:ArkType offers TypeScript-native syntax for runtime validation:ArkType's type syntax works with sValidatorArkType's type syntax works with sValidatorArkType syntax

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}`,
  })
})