Проверка данных с zod
Данные от клиента нельзя брать на веру: заголовок статьи может оказаться пустым, номер — словом, а поля — вовсе отсутствовать. Библиотека zod описывает ожидаемую форму данных схемой, а пакет @hono/zod-validator превращает схему в middleware, которое проверяет запрос до обработчика.
Установка
npm install zod @hono/zod-validatorСхема статьи
Схемы удобно держать в отдельном файле. Создайте src/schemas.ts:
// src/schemas.ts
import { z } from 'zod'
export const postSchema = z.object({
title: z.string().min(1).max(200),
body: z.string().min(1),
})Схема говорит: ожидается объект с непустыми строками title (не длиннее 200 символов) и body.
Проверка тела запроса
Подключите валидатор в маршрут создания статьи:
// src/routes/posts.ts
import { Hono } from 'hono'
import { zValidator } from '@hono/zod-validator'
import { postSchema } from '../schemas.js'
const posts = new Hono()
posts.post('/', zValidator('json', postSchema), (c) => {
const data = c.req.valid('json')
return c.json({ id: 3, ...data }, 201)
})
export default postszValidator('json', postSchema) читает JSON-тело и проверяет его. Если данные подходят, c.req.valid('json') возвращает их с типом { title: string; body: string }. Если нет — обработчик не вызывается, а клиент получает код 400 и описание ошибок.
curl -X POST http://localhost:3000/posts \
-H "Content-Type: application/json" \
-d '{"title": ""}'Ответ придёт с кодом 400: в нём будет сказано, что title слишком короткий, а body отсутствует.
Что можно проверять
Первый аргумент zValidator — откуда брать данные:
| Цель | Что проверяется | Получение |
|---|---|---|
json | JSON-тело | c.req.valid('json') |
form | данные формы | c.req.valid('form') |
query | строка запроса | c.req.valid('query') |
param | параметры пути | c.req.valid('param') |
header | заголовки | c.req.valid('header') |
Число из адреса
Параметры приходят строками. z.coerce.number() сам превратит строку в число:
// src/routes/posts.ts
const idSchema = z.object({ id: z.coerce.number().int().positive() })
posts.get('/:id', zValidator('param', idSchema), (c) => {
const { id } = c.req.valid('param')
return c.json({ id })
})Импортируйте z из zod в начале файла. /posts/5 вернёт {"id":5} с числом, а /posts/abc — ошибку 400.
Свой ответ при ошибке
Третий аргумент — функция, которая получает результат проверки:
// src/routes/posts.ts
posts.post(
'/',
zValidator('json', postSchema, (result, c) => {
if (!result.success) {
return c.json({ error: 'Проверьте заголовок и текст статьи' }, 400)
}
}),
(c) => c.json(c.req.valid('json'), 201),
)Так клиент получит понятное сообщение вместо подробного отчёта zod.
Частая ошибка
После подключения валидатора данные снова читают через await c.req.json(). Такие данные не проверены и не имеют типа. Берите их только из c.req.valid('json') — именно там лежит результат проверки.