cat logo

Hono.js: The Easy Path to Efficient APIs

5 min read
0
hono.js bun backend testing middleware
Hono.js: The Easy Path to Efficient APIs

Hono.js: The Easy Path to Efficient APIs

When it comes to simple backend development, Express.js comes to mind. However, in 2024 it’s considered outdated as faster alternatives exist. Greetings dear readers! Today I’ll introduce you to Hono.js.

Hono.js is a tiny, simple, and ultra-fast framework built on web standards. Under the hood, it offers TypeScript support and comfortable local development. Hono.js works across different JavaScript runtimes: Cloudflare Workers, Deno, Bun, Vercel, Netlify, and even Node.js.

Hono.js solves the problem of developing high-performance lightweight web applications by minimizing overhead and complexity. Many developers find that popular frameworks like Express.js or Koa.js, while feature-rich, can be bloated and heavy for simple tasks—especially in projects where performance and minimalism matter. Hono.js specifically targets these issues, offering an ultralight framework that enables rapid server application development without unnecessary dependencies and with minimal code.

Key Features of Hono.js

Lightweight

Hono.js stands out for its featherweight size—it’s one of the most compact web frameworks available. Its minimal source code significantly reduces overhead. Unlike larger frameworks, Hono.js isn’t loaded with built-in modules that might be redundant for small projects. For comparison: while Express.js weighs 2MB, Hono.js is just 14KB.

Ease of Use

Hono.js is built on simple and intuitive concepts, making it easy to learn even for beginners. It’s minimalist yet provides all core features needed for web development: routing, request handling, responses, middleware support, and URL parameters.

Here’s a minimal Hono.js application example:

import { Hono } from 'hono'
const app = new Hono()

app.get('/', (c) => c.text('Hono!'))

export default app

The equivalent Express.js implementation is noticeably heavier:

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}`)
})

I think you’re liking Hono.js already, but there’s more! Now let’s move to installation and setup. We’ll use Bun.js as our runtime.


Installation and Initial Setup

  1. Project Initialization and Basic Server
    Start by using Bun to initialize your project:
bun init

Before implementing Hono.js, let’s create a simple HTTP server with Bun in index.tsx:

// index.tsx
Bun.serve({
  fetch: (req) => {
    return new Response('Hello from Bun!')
  },
  port: process.env.PORT || 3030,
})

Response output on port 3030

Installing and Integrating Hono.js

Install Hono.js after project initialization:

bun i hono

Import Hono and create a GET endpoint returning simple JSON:

// 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,
})

We’ve replaced the custom function by handing control to Hono. Now Bun will process HTTP requests through Hono’s API:
Response on the same port

Group Routing in Hono.js

Hono.js documentation introduces group routing via route(). Let’s create books.ts:

// 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

export default book

Import and mount the router in index.tsx:

// index.tsx
app.route('/book', bookRouter)

"Book list" at /book

What is “c” in the arguments?

You might have noticed c instead of req—this is short for context object (documented here). This object handles all incoming/outgoing data. Hono allows returning responses not just as JSON, but also HTML, streams, etc.

Middleware

Middleware executes before/after handlers. We can intercept requests pre-dispatch or modify responses post-dispatch:

// index.tsx
import { logger } from 'hono/logger'

app.use('*', logger())

Executing requests shows detailed logs in VS Code:
Request/response logging with status codes

JSX Rendering

While hono/jsx works client-side, it also supports server-side rendering:

// page.tsx
import { Hono } from 'hono'
import type { FC } from 'hono/jsx'

const Layout: FC = (props) => {
  return (
    <html>
      <body>{props.children}</body>
    </html>
  )
}

const Top: FC<{ messages: string[] }> = (props) => {
  return (
    <Layout>
      <h1>Hello Hono!</h1>
      <ul>
        {props.messages.map((message) => (
          <li>{message}!!</li>
        ))}
      </ul>
    </Layout>
  )
}

export default Top

Update tsconfig.json for JSX support:

{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "hono/jsx"
  }
}

Render the component in your route:

// index.tsx
import Top from './page.tsx'

app.get('/', (c) => {
  const messages = ['Good Morning', 'Good Evening', 'Good Night']
  return c.html(<Top messages={messages} />)
})

Server-side rendering output

This resembles SSR in Next.js/Remix but is significantly lighter. Hono also supports async components, Suspense, and more.

Testing

Testing Hono applications is straightforward. First, extract core logic to app.ts:

// index.test.ts
import { expect, test, describe } from 'bun:test'
import app from './app'

describe('Example', () => {
  test('GET /hello', async () => {
    const res = await app.request('/hello')
    expect(res.status).toBe(200)
    expect(await res.json()).toEqual({ hello: 'world' })
  })
})

Run tests with:

bun test

Passed tests output


Surprisingly, despite its popularity this year, Hono.js remains largely uncovered in the Russian tech sphere. I encourage you to reconsider this excellent Express.js alternative.

Hono.js offers much more: validation, RPC, best practices, and more. Check the official resources below!

Useful links:
Official Hono.js site — https://hono.dev