Hono — небольшой веб-фреймворк на TypeScript для Cloudflare Workers, Deno, Bun, Node.js и других сред с Web API. Он даёт роутер, middleware и удобный контекст запроса, оставаясь близким к стандартным Request и Response.

Разберём маршруты, валидацию, bindings на edge, тесты и деплой. Примеры рассчитаны на Hono 4.x и Node.js 20+.

Почему Hono подходит для edge

Edge-платформы запускают код близко к пользователю и ограничивают окружение: нет файловой системы и долгоживущих процессов, доступны только Web API и привязки платформы. Hono не тянет Node-специфичные зависимости и собирается в компактный бандл.

  • Роутер с параметрами, wildcard и регулярными ограничениями.
  • Готовые middleware: CORS, логирование, JWT, кеш, сжатие.
  • Типизированный клиент RPC для фронтенда.
  • Адаптеры для разных рантаймов без смены кода приложения.

Состояние между запросами не хранится в памяти, а процессорное время и размер бандла лимитированы тарифом. Для данных используйте KV, D1, R2 или внешнюю базу.

Создание проекта

Генератор предложит шаблон и рантайм: для Cloudflare выбирайте Workers, для VPS — Node.js.

npm create hono@latest my-api
cd my-api
npm install
npm run dev

В шаблоне есть точка входа и пример маршрута. Для Node.js используется @hono/node-server, для Workers — wrangler dev. Включите строгий TypeScript: он ловит ошибки в параметрах и bindings.

npm install hono @hono/node-server
npm run build
npx wrangler dev
npx wrangler deploy

Маршруты и контекст

Маршрут получает контекст c: из него читают параметры, заголовки и тело, в него пишут ответ. Параметры пути объявляются двоеточием, ограничения — регулярным выражением в фигурных скобках.

import { Hono } from 'hono'

const app = new Hono()

app.get('/', (c) => c.text('ok'))
app.get('/users/:id{[0-9]+}', (c) => {
  const id = c.req.param('id')
  return c.json({ id: Number(id) })
})
app.get('/search', (c) => c.json({ q: c.req.query('q') ?? '' }))

Группы маршрутов подключаются через app.route('/api', api), базовый путь задаётся app.basePath('/v1'). Вложенные приложения собираются из отдельных экземпляров Hono.

Тело запроса читается один раз: получите объект через await c.req.json() и передайте дальше. Ошибку разбора JSON обрабатывайте явно, иначе клиент получит 500 вместо 400.

Middleware и обработка ошибок

Middleware — функция, которая получает контекст и next. Она может изменить запрос, добавить заголовки или прервать цепочку. Порядок регистрации важен: он определяет порядок выполнения.

import { logger } from 'hono/logger'
import { cors } from 'hono/cors'
import { secureHeaders } from 'hono/secure-headers'

app.use('*', logger())
app.use('/api/*', cors({ origin: 'https://example.com' }))
app.use('*', secureHeaders())
app.onError((err, c) => {
  console.error(err)
  return c.json({ error: 'internal' }, 500)
})

Для авторизации есть basicAuth, bearerAuth и jwt. JWT-middleware проверяет подпись и кладёт payload в переменные контекста.

import { jwt } from 'hono/jwt'

app.use('/admin/*', jwt({ secret: 'CHANGE_ME' }))
app.get('/admin/me', (c) => c.json(c.get('jwtPayload')))

Секрет в примере — заглушка. В продакшене берите значение из c.env или хранилища секретов и не логируйте токены.

Валидация и типизированный клиент

Схемы удобно описывать через Zod и подключать @hono/zod-validator. Валидатор возвращает 400 с деталями и передаёт проверенные данные в обработчик.

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

const schema = z.object({ name: z.string().min(1), age: z.number().int() })

app.post('/users', zValidator('json', schema), (c) => {
  const body = c.req.valid('json')
  return c.json(body, 201)
})

Типы можно прокинуть на клиент через RPC. Экспортируйте тип приложения и используйте hc: клиент будет знать пути и формы ответов.

export type AppType = typeof app

const client = hc<AppType>('https://api.example.com')
const res = await client.users.$post({ json: { name: 'Ann', age: 30 } })
const data = await res.json()

RPC не заменяет контракт для внешних потребителей: для публичного API нужна OpenAPI-схема. Зато в монорепозитории он убирает рассинхрон фронтенда и бэкенда.

Bindings и работа с данными на edge

На Cloudflare окружение приходит в c.env. Типизируйте его через generic Hono<{ Bindings: Env }>, чтобы редактор подсказывал имена привязок.

type Env = {
  Bindings: { KV: KVNamespace; DB: D1Database }
}

const app = new Hono<Env>()
app.get('/cache/:key', async (c) => {
  const value = await c.env.KV.get(c.req.param('key'))
  return value ? c.text(value) : c.notFound()
})

KV подходит для чтения редкозаписываемых значений, D1 — для SQL, R2 — для файлов. Помните о согласованности: KV распространяется с задержкой, поэтому счётчики и блокировки на нём строить нельзя. Для транзакций используйте D1 или внешнюю PostgreSQL.

Фоновые задачи запускаются через c.executionCtx.waitUntil(promise); платформа дождётся завершения в пределах лимита.

Тесты и деплой

Приложение можно вызвать без сети: app.request() создаёт запрос и возвращает ответ — быстрый способ проверить маршруты в тестах.

import { describe, it, expect } from 'vitest'

describe('api', () => {
  it('returns ok', async () => {
    const res = await app.request('/')
    expect(res.status).toBe(200)
  })
})

Для VPS используйте адаптер Node.js и systemd: приложение слушает localhost, nginx принимает TLS.

import { serve } from '@hono/node-server'
serve({ fetch: app.fetch, port: 3000, hostname: '127.0.0.1' })

Юнит systemd:

[Unit]
Description=my-api
After=network.target

[Service]
WorkingDirectory=/srv/my-api
ExecStart=/usr/bin/node dist/index.js
Restart=always
User=www-data

[Install]
WantedBy=multi-user.target

После systemctl enable --now my-api проверьте curl и journalctl -u my-api -f. Для Cloudflare смотрите wrangler tail.

Чек-лист перед продакшеном

  • Секреты вынесены в bindings или переменные окружения.
  • Включены CORS только для нужных origin и security-заголовки.
  • Ограничен размер тела запроса и настроен таймаут.
  • Ошибки логируются без персональных данных и токенов.
  • Есть тесты на ключевые маршруты и валидацию.
  • Понятны лимиты процессорного времени, памяти и времени выполнения платформы.

Итог

Hono даёт минимальный, но достаточный набор для API на edge: роутер, middleware, валидацию и типизированный клиент. Он не скрывает Request и Response, поэтому код легко переносится между рантаймами. Начните с одного приложения и теста через app.request, затем добавьте bindings и деплой. Если проект требует файловой системы, фоновых демонов и тяжёлых зависимостей, проще взять Node.js на VPS — Hono там тоже работает.

Насколько публикация полезна?

Нажмите на звезду, чтобы оценить!

Средняя оценка 0 / 5. Количество оценок: 0

Оценок пока нет. Поставьте оценку первым.

Поделиться: Telegram ВКонтакте