Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Проверка данных с 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 posts

zValidator('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 — откуда брать данные:

ЦельЧто проверяетсяПолучение
jsonJSON-тело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') — именно там лежит результат проверки.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.