Когда речь идет о разработке простого бэкенда, то в голову приходит 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
})
Вывод ответа на 3030-ом порту
Вывод ответа на 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)
"Список" книжек на /book
"Список" книжек на /book

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

Комментарии (11)


  1. Nurked
    25.08.2024 23:01
    +4

    Аа? Вы вообще серьёзно?

    Ну, во первых, ваша идея о том, что код на экспрессе длинее (на целых две линии) "очередного-убийцы-экспресса" она основана просто на том, что вы добавили два определения констант в начало вашего кода.

    Во-вторых, добро пожаловать в 2024 год. Мы тут код пишем, а не во фреймворки играемся. Кто вообще, в своём уме, будет писать экспресс вручную? Этим занимаются копилот, ГПТ и Клауд. Экспресс отлично работает, и для него есть документация. Код экспресса геренируется различными чат-ботами.

    Нафига что-то улучшать? Жалко бойлерплейта? Ну так за нас уже пишут.

    Если хочется совсем красиво, то устанавливаешь Zed, добавляешь туда апи ключ от своего чат-бота, даёшь ему список методов, и говоришь, чтобы он написал тебе всё нужное.

    Итог?

      router.post("/login", execute<Requests.UserLogin>(h.handleUserLogin));
      router.post("/check", execute<Requests.CheckUser>(h.checkUser));

    Пожалуйста, строгий тайпчекинг параметров входного JSON и раутинг одной строкой.

    Надо не фреймворками заниматься, а писать полезный код. Благо, делать это сейчас ещё проще, чем когда-либо.


    1. zoto_ff
      25.08.2024 23:01
      +3

      экспресс не нужно убивать, он давно уже мёртвый. все, кому надо, пересели на Fastify или что-то получше. на экспрессе сидят только конторы с легаси кодом и не самые далёкие разработчики


    1. ilja903
      25.08.2024 23:01

      Тайпскрипт и jsx интеграция у хоно понятные и удобные. Экспресс это коллбеки сплошные, асинк там не впилить.


  1. Vottakonotak
    25.08.2024 23:01

    За новость спасибо. Буду знать. Но в сравнении надо показывать, что под капотом. Например, загрузка файлов. Как это выполняется? С помощью всеми любимого busboy или что-то своё придумали. Да, и nodejs может без фреймворков обойтись. Интересно было бы узнать, hono быстрее, чем чистая нода обрабатывает запросы. Я, например, отказался от фреймворков и на чистой ноде работаю, поэтому меня и интересует сравнение чистой nodejs с данным фреймворком.


  1. sonytruelove
    25.08.2024 23:01
    +1

    Node — H3 — Nuxt
    Node — Express/Fastify — Nest
    Node — Next
    Bun — Hono  — ?
    Не знаком с фреймворками построенными на Hono. А как самостоятельный фреймворк для проды выглядит не очень... Пока нет хедлайнера, Hono вряд‑ли получит нужную поддержку и охваты. Кстати, напомнило Ruby с их философией, загуглил — тоже Япония!
    Как туториал статья выполняет свои цели. На удивление, действительно статей о Hono на хабре раз-два и обчелся. Спасибо автору за просвещение и популяризацию!


  1. poimtsev
    25.08.2024 23:01
    +3

    Я бы рекомендовал еще вот такой фреймворк посмотреть https://elysiajs.com - напоминает hono, но много плагинов и других свистелок


    1. zoto_ff
      25.08.2024 23:01

      +, использую элизию у себя на проде

      имхо, она удобнее. и производительнее на ~20%


      1. sonytruelove
        25.08.2024 23:01

        Стабильная версия вышла в марте, смело уже иметь проду на ней(Переучивались с какого-то стека или давно за Elisya следите?). Bun+Elysia это шустро и круто, но мне не хватает CLI (хотя я только что нашел что-то подобное).
        В целом думаю, что Bun может потягаться с мастодонтами, и вижу за ним будущее JS Backend разработки.


    1. TerrniT
      25.08.2024 23:01
      +1

      Как раз начал писать про Elysia. Если есть предложения по содержанию, готов прислушаться


      1. sonytruelove
        25.08.2024 23:01

        Лично мне интересно бы узнать про работу Sucrose под капотом. Но боюсь это может не подойти под ваши идеи статьи, но всегда ведь можно написать еще одну. Вообщем я бы с удовольствием почитал!


  1. MuradSky
    25.08.2024 23:01

    Нее спасибо, вместо ХАНА уж лучше nitro.js или Fastify.