Розробка бекенду сайту на Node.js (Hono)
Hono — один з небагатьох фреймворків, написаних для роботи на кількох runtime одночасно: Node.js, Deno, Bun, Cloudflare Workers, AWS Lambda Edge. Один код — кілька платформ без адаптерів. Це головна причина його вибирати, коли проект може жити на edge або потрібна гнучкість по платформі.
Другий аргумент — продуктивність. Hono використовує RegExpRouter — один з найшвидших алгоритмів маршрутизації: O(1) для статичних маршрутів, O(n) для параметричних. На Bun обганяє Fastify в бенчмарках.
Базова структура додатку
import { Hono } from 'hono'
import { cors } from 'hono/cors'
import { logger } from 'hono/logger'
import { prettyJSON } from 'hono/pretty-json'
import { secureHeaders } from 'hono/secure-headers'
const app = new Hono()
// Вбудовані middleware
app.use('*', logger())
app.use('*', secureHeaders())
app.use('/api/*', cors({
origin: process.env.ALLOWED_ORIGINS?.split(',') ?? '*',
credentials: true
}))
// Обробка помилок
app.onError((err, c) => {
console.error(err)
return c.json({ error: err.message }, 500)
})
app.notFound((c) => c.json({ error: 'Not found' }, 404))
export default app
Маршрутизація та групування
import { Hono } from 'hono'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'
const products = new Hono()
const createProductSchema = z.object({
name: z.string().min(2).max(255),
price: z.number().positive(),
categoryId: z.number().int()
})
products.get('/', async (c) => {
const { page = '1', limit = '20' } = c.req.query()
const db = c.get('db')
const items = await db.query(
'SELECT * FROM products LIMIT $1 OFFSET $2',
[Number(limit), (Number(page) - 1) * Number(limit)]
)
return c.json(items.rows)
})
products.post('/',
zValidator('json', createProductSchema),
async (c) => {
const body = c.req.valid('json') // повністю типізовано
const db = c.get('db')
// ...
return c.json({ id: newProduct.id }, 201)
}
)
products.get('/:id', async (c) => {
const id = c.req.param('id')
// ...
})
// Монтування з префіксом
const api = new Hono().basePath('/api/v1')
api.route('/products', products)
api.route('/users', users)
app.route('', api)
Аутентифікація через JWT
import { jwt } from 'hono/jwt'
import { getCookie, setCookie } from 'hono/cookie'
const authMiddleware = jwt({
secret: process.env.JWT_SECRET!,
cookie: 'access_token' // підтримує JWT з cookie
})
// Захищені маршрути
const protected = new Hono()
protected.use('*', authMiddleware)
protected.get('/profile', (c) => {
const payload = c.get('jwtPayload') // типізований payload
return c.json({ userId: payload.sub })
})
// Endpoint входу
app.post('/auth/login', zValidator('json', loginSchema), async (c) => {
const { email, password } = c.req.valid('json')
const user = await UserService.verifyCredentials(email, password)
if (!user) return c.json({ error: 'Invalid credentials' }, 401)
const token = await sign({ sub: user.id, role: user.role }, process.env.JWT_SECRET!)
setCookie(c, 'access_token', token, {
httpOnly: true,
secure: true,
sameSite: 'Strict',
maxAge: 60 * 60 * 24 * 7
})
return c.json({ user: { id: user.id, email: user.email } })
})
Middleware та контекст
Hono використовує c.set() / c.get() для передачі даних через middleware — аналог ctx.state у Koa:
// Підключення БД через middleware
app.use('*', async (c, next) => {
c.set('db', pgPool)
await next()
})
// Типізація змінних контексту
type Env = {
Variables: {
db: Pool
user: { id: number; role: string }
}
}
const app = new Hono<Env>()
Edge-деплой на Cloudflare Workers
Одна з ключових переваг Hono — деплой на Cloudflare Workers без змін коду:
// wrangler.toml
// name = "my-api"
// compatibility_date = "2024-01-01"
// src/index.ts — той самий код
export default app // Hono сумісний з Workers
// Доступ до Workers KV, D1, R2 через bindings
app.get('/cache/:key', async (c) => {
const kv = c.env.MY_KV // Cloudflare KV namespace
const value = await kv.get(c.req.param('key'))
return value ? c.text(value) : c.notFound()
})
Це корисно для API, які повинні працювати з мінімальною затримкою у всьому світі — CDN-edge замість одного дата-центру.
RPC-клієнт
Hono підтримує типізований клієнт — фронтенд отримує типи з серверного коду без окремої схеми:
// server/routes/users.ts
const users = new Hono()
.get('/', ..., (c) => c.json({ users: [] }))
.post('/', ..., (c) => c.json({ id: 1 }, 201))
export type UsersRoutes = typeof users
// client/api.ts (Next.js, React та ін.)
import { hc } from 'hono/client'
import type { UsersRoutes } from '../server/routes/users'
const client = hc<UsersRoutes>('http://localhost:3000')
const res = await client.users.$get()
const data = await res.json() // повністю типізована відповідь
Коли використовувати Hono
Hono добре підходить для: API з можливим edge-деплоєм, проектів з TypeScript від початку, BFF (Backend For Frontend) шару поряд з React/Next.js, мікросервісів з мінімальним footprint. Менш підходить для: складних монолітів з багатим ORM, проектів з усталеною експертизою Express/Koa в команді.
Терміни розробки
- Налаштування проекту + базові маршрути — 2–3 дні
- Auth + валідація — 3–5 днів
- Бізнес-логіка — 1–3 тижні залежно від обсягу
- Edge-деплой та конфігурація Cloudflare — 2–4 дні додатково
Hono — швидкий старт з хорошою типізацією. Для невеликого API сайту повний цикл: 3–6 тижнів.







