Skip to main content

Overview

Hono provides a powerful and flexible validation system that allows you to validate incoming request data from various sources including JSON bodies, form data, query parameters, headers, and cookies.

Basic Validation

The validator middleware extracts data from different parts of the request and validates it using a custom validation function.

Validation Targets

You can validate data from these sources:
  • json - JSON request body
  • form - Form data (multipart or URL-encoded)
  • query - Query parameters
  • param - Path parameters
  • header - Request headers
  • cookie - Cookies

Basic Example

import { Hono } from 'hono'
import { validator } from 'hono/validator'

const app = new Hono()

app.post(
  '/posts',
  validator('json', (value, c) => {
    const parsed = value as { title: string; body: string }
    if (!parsed.title || typeof parsed.title !== 'string') {
      return c.text('Invalid title', 400)
    }
    if (!parsed.body || typeof parsed.body !== 'string') {
      return c.text('Invalid body', 400)
    }
    return parsed
  }),
  (c) => {
    const { title, body } = c.req.valid('json')
    return c.json({ message: 'Created', title, body })
  }
)

Query Parameter Validation

Validate URL query parameters:
app.get(
  '/search',
  validator('query', (value, c) => {
    const query = value as { q: string; limit?: string }
    if (!query.q) {
      return c.text('Missing search query', 400)
    }
    return {
      q: query.q,
      limit: query.limit ? parseInt(query.limit) : 10,
    }
  }),
  (c) => {
    const { q, limit } = c.req.valid('query')
    return c.json({ results: [], query: q, limit })
  }
)

Form Data Validation

Validate form data submissions:
app.post(
  '/upload',
  validator('form', (value, c) => {
    const form = value as { name: string; file: File }
    if (!form.name) {
      return c.text('Name is required', 400)
    }
    if (!form.file || !(form.file instanceof File)) {
      return c.text('Valid file is required', 400)
    }
    return form
  }),
  (c) => {
    const { name, file } = c.req.valid('form')
    return c.json({ message: 'Uploaded', name, size: file.size })
  }
)

Multiple Validators

Chain multiple validators to validate different parts of the request:
app.post(
  '/posts/:id/comments',
  validator('param', (value, c) => {
    const id = parseInt(value.id as string)
    if (isNaN(id)) {
      return c.text('Invalid ID', 400)
    }
    return { id }
  }),
  validator('json', (value, c) => {
    const data = value as { comment: string }
    if (!data.comment || data.comment.length < 1) {
      return c.text('Comment is required', 400)
    }
    return data
  }),
  (c) => {
    const { id } = c.req.valid('param')
    const { comment } = c.req.valid('json')
    return c.json({ postId: id, comment, created: true })
  }
)

Using Zod

For more complex validation, use Zod with Hono’s validator:
import { z } from 'zod'
import { zValidator } from '@hono/zod-validator'

const schema = z.object({
  title: z.string().min(1).max(100),
  body: z.string().min(1),
  tags: z.array(z.string()).optional(),
  published: z.boolean().default(false),
})

app.post('/posts', zValidator('json', schema), (c) => {
  const data = c.req.valid('json')
  // data is fully typed!
  return c.json({ message: 'Created', data })
})

Custom Error Messages

Return custom error responses:
import { HTTPException } from 'hono/http-exception'

app.post(
  '/api/users',
  validator('json', (value, c) => {
    const data = value as { email: string; password: string }
    
    if (!data.email || !data.email.includes('@')) {
      throw new HTTPException(400, { 
        message: 'Invalid email address' 
      })
    }
    
    if (!data.password || data.password.length < 8) {
      throw new HTTPException(400, { 
        message: 'Password must be at least 8 characters' 
      })
    }
    
    return data
  }),
  (c) => {
    const user = c.req.valid('json')
    return c.json({ message: 'User created', email: user.email })
  }
)

Validation Utilities

The validator automatically handles:
  • Content-Type detection for JSON (application/json)
  • Form data parsing (both multipart/form-data and application/x-www-form-urlencoded)
  • Array handling in form data (fields ending with [])
  • Type safety with TypeScript

Best Practices

Never trust data from clients. Always validate and sanitize input before processing.
Leverage TypeScript’s type system along with validation to catch errors early.
Provide clear, actionable error messages to help API consumers fix issues.
For complex validation logic, use libraries like Zod, Valibot, or TypeBox with Hono validators.

Build docs developers (and LLMs) love