Когда речь идет о разработке простого бэкенда, то в голову приходит Express.js. Однако в 2024 году он считается устаревшим, так как есть шустрые альтернативы. Приветствую вас, дорогие читатели и сегодня расскажу о Hono.js.
Hono.js — маленькой, простой и сверхбыстрый фрейморк, построенный на веб-стандартах. Под его капотом поддержка TypeScript и комфортная разработка в локальной среде. Hono.js работает в разных рантаймах JavaScript: Cloudflare Workers, Deno, Bun, Vercel, Netlify, в том числе и Node.js.
Hono.js решает проблему разработки высокопроизводительных и легковесных веб-приложений, минимизируя накладные расходы и сложность. Многие разработчики сталкиваются с тем, что популярные фреймворки, такие как Express.js или Koa.js, хотя и предоставляют широкие возможности, бывают избыточными и тяжелыми для простых задач, особенно в проектах, где важна производительность и минимализм. Собственно Hono.js ориентирован на решение этих проблем, предлагая ультралегкий фреймворк, с помощью которого быстро создаваются серверные приложения без лишних зависимостей и с минимальным кодом.
Особенности Hono.js
Легковесность
Hono.js выделяется легковесностью — это один из самых компактных веб-фреймворков на рынке. Размер его исходного кода минимален, что даёт возможность значительно снизить накладные расходы при его использовании. В отличие от крупных фреймворков, Hono.js не загружен множеством встроенных модулей и функций, которые могут быть избыточными для небольших проектов. Для сравнения Express.js, который весит 2 МБ, Hono.js весит всего лишь 14 КБ.
Простота использования
Hono.js построен на простых и интуитивных концепциях, что делает его легким в освоении и использовании даже для новичков. Он минималистичен, но при этом предоставляет все основные функции, необходимые для создания веб-приложений, такие как маршрутизация, обработка запросов и ответы, поддержка middleware и работа с параметрами URL.
Ниже представлен простой пример минимального приложения на Hono.js:
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.text('Hono!'))
export default app
Если тоже самое написать на Express.js, выйдет многозатратно:
const express = require('express')
const app = express()
const port = 3000
app.get('/', (req, res) => {
res.send('Hello World!')
})
app.listen(port, () => {
console.log(Example app listening on port ${port})
})
Думаю, вам нравится Hono.js, но это ещё не всё, впереди много интересного. Теперь перейдем к его установке и начальной настройки. В качестве рантайма буду использовать Bun.js .
Установка и начальная настройка
1) Инициализация проекта и создание простого сервера
На старте буду использовать Bun для инициализации проекта:
bun init
Перед тем, как перейдем к Hono.js
, давайте создадим простой HTTP-сервер через Bun в index.tsx:
// index.tsx
Bun.serve({
fetch: (req) => {
return new Response('Hello from Bun!')
},
port: process.env.PORT || 3030
})
2) Установка и подключение Hono.js
После инициализации проекта сразу же и установим Hono.js
:
bun i hono
Подключаем Hono
и пишем GET-запрос на получение ответа в виде простого JSON, чутка меняя то, что написали через Bun:
// index.tsx
import { Hono } from 'hono'
const app = new Hono()
app.get('/hello', (c) => {
return c.json({ hello: 'world' })
})
Bun.serve({
fetch: app.fetch,
port: process.env.PORT || 3030,
})
Таким образом, мы избавляемся от пользовательской функции , передав это всё Hono. Теперь HTTP-запросы, поступающие на сервер Bun, будет обрабатываться этой платформой, что нам дает гораздо удобнее API:
3) Групповая маршрутизация Hono.js
Следуя по офиц. документации Hono.js
, можно увидеть раздел о групповом роутинге, с помощью которого группируются маршруты через экземпляр Hono и добавлять их в основное приложение методом route
.
Создадим books.ts
и воспользуемся примером оттуда. Не забудем экспортировать book
по умолчанию:
// routes/books.ts
import { Hono } from 'hono'
const book = new Hono()
book.get('/', (c) => c.text('List Books')) // GET /book
book.get('/:id', (c) => {
// GET /book/:id
const id = c.req.param('id')
return c.text('Get Book: ' + id)
})
book.post('/', (c) => c.text('Create Book')) // POST /book
const app = new Hono()
app.route('/book', book)
export default book
Далее импортируем bookRouter из book.ts и воспользуемся ним следующим образом. конечном итоге прилетает ответ списка книг:
// index.tsx
app.route('/book', bookRouter)
4) Что же за "c" в аргументах???
Однако, как вы могли заметить, вместо req
используется c
, что сокращенно означает объект context
. Об этом написано в документации Hono.js
.
По факту все исходящие и входящие данные обрабатываются данным объектом. Hono
дает возможность возвращать ответы не только через json-формат, но и множеством других (например, html-формат).
5) Middleware
Middleware работает перед/после обработчика. Мы можем получить запрос до диспатча или манипулировать ответом после диспатча:
// index.tsx
import { logger } from 'hono/logger'
app.use('*', logger())
Выполняя различные запросы, в консоли редактора VS Code можно увидеть, как описывается то, что отправляется и что мы получаем с статусом кода:
5) Рендеринг JSX
Хотя hono/jsx
работает на клиенте, его также можно использовать его при рендеринге контента на стороне сервера
В разделе про JSX есть пример функционального React-компонента, попробуем с ней отрендрить на стороне сервера:
// page.tsx
import { Hono } from 'hono'
import type { FC } from 'hono/jsx'
const app = new Hono()
const Layout: FC = (props) => {
return (
<html>
<body>{props.children}</body>
</html>
)
}
const Top: FC<{ messages: string[] }> = (props: {
messages: string[]
}) => {
return (
<Layout>
<h1>Hello Hono!</h1>
<ul>
{props.messages.map((message) => {
return <li>{message}!!</li>
})}
</ul>
</Layout>
)
}
export default Top
Далее в конфиге TypeScript необходимо изменить некоторые настройки, чтобы JSX заработал:
// tsconfig.json
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "hono/jsx",
}
Импортируем компонент <Top />
в корневой файл и отрендрим простенькую разметку:
// index.tsx
import Top from './page.tsx'
app.get('/', (c) => {
const messages = ['Good Morning', 'Good Evening', 'Good Night']
return c.html(<Top messages={messages} />)
})
Это напоминает SSR в Next.js или Remix.js, но это решение является легким. Hono так же поддерживает и другие фичи, например асинхронные компоненты, Suspense и тд.
6) Тестирование
Тестирование — это важно, но протестировать приложения Hono — несложно. Способ создания тестовой среды отличается в каждой среде выполнения, но основные шаги одинаковы. Перейдем непосредственно к тестированию.
Для удобства я вынесу код в отдельный файл app.tsx
.
// index.test.ts
import { expect, test, describe } from 'bun:test'
import app from './app'
describe('Example', () => {
test('GET /posts', async () => {
const res = await app.request('/hello')
expect(res.status).toBe(200)
expect(await res.json()).toEqual({ hello: 'world' })
})
expect(2 + 2).toBe(4)
})
// index.tsx
import app from './app'
Bun.serve({
fetch: app.fetch,
port: process.env.PORT || 3030,
})
В журнале консоли вводим команду для того, чтобы посмотреть, прошли ли тесты или нет:
Таким образом, легко и просто можно протестировать своё API.
Удивительно, что Hono.js, став достаточно популярным в этом году, не освещен в российском сегменте, так как про него практически никаких статей и видео. Так что вы можете пересмотреть для себя отличную альтернативу над Express.js.
В Hono.js есть много всего, но в этом вы можете убедиться самостоятельно: валидация, RPC, Best Practices и многое другое.
До скорых встреч, спасибо за внимание. Надеюсь, вам понравилась моя статья, для меня это очень важно!
Полезные ссылки:
Официальная сайт Hono,js — https://hono.dev