Hono: Satu Basis Kode API untuk Workers, Bun, dan Node

Foto oleh LensaCibi via Wikimedia Commons (CC BY-SA 4.0)
Hono adalah framework web kecil tanpa dependensi yang dibangun di atas Web Standards seperti objek Request dan Response dari fetch API. Berbeda dari Express yang terikat pada modul http Node, Hono tidak membawa asumsi runtime, sehingga aplikasi yang sama berjalan di Cloudflare Workers, Bun, Node, dan Deno. Per Juli 2026 versinya v4.12 dan preset hono/tiny-nya di bawah 12kB.
Ya. Aplikasi Hono Anda adalah objek dengan metode fetch yang menerima Request dan mengembalikan Response. Setiap runtime mendapat adapter tipis — Workers dan Bun mengekspor aplikasi secara langsung, sedangkan Node memakai paket @hono/node-server. Kode aplikasinya tidak pernah berubah; hanya file entry kecil per platform yang berubah.
Hono menginferensi tipe rute Anda langsung dari kode server. Anda mengekspor satu tipe TypeScript (biasanya typeof rute Anda) dan klien hc membacanya, memberi frontend input dan output yang bertipe penuh. Tidak ada spesifikasi OpenAPI dan tidak ada langkah codegen, tetapi inferensi hanya bekerja jika Anda merangkai handler rute menjadi satu ekspresi yang tidak terputus.
RegExpRouter bawaan Hono mengompilasi semua rute terdaftar menjadi satu ekspresi reguler, sehingga mencocokkan path yang masuk mendekati waktu konstan berapa pun jumlah rutenya. Ini menghindari penelusuran radix tree di setiap permintaan dan, dalam benchmark yang dipublikasikan, jauh lebih cepat daripada router find-my-way yang dipakai Fastify.
Hono sengaja tidak beropini tentang lapisan di atas HTTP. Jika Anda ingin dependency injection bawaan, decorator, dan arsitektur modular yang kaku, framework serba-lengkap seperti NestJS lebih cocok. Selain itu, rute apa pun yang menjangkau API spesifik-platform — binding KV Cloudflare atau stream Node — berhenti menjadi portabel, jadi simpan akses itu di balik antarmuka yang bisa ditukar.

Foto oleh LensaCibi via Wikimedia Commons (CC BY-SA 4.0)
Ringkasan Utama
Hono adalah framework web tanpa dependensi yang dibangun di atas Web Standards. Anda menulis satu API menggunakan objek Request dan Response yang sudah disediakan platform, lalu menjalankannya tanpa perubahan di Cloudflare Workers, Bun, Node, dan Deno hanya dengan menukar adapter tipis. Klien RPC bertipenya memberi frontend keamanan tipe penuh tanpa codegen.
Saya sudah cukup sering merilis layanan Express untuk tahu pajak yang diam-diam dibebankannya: ia terikat pada modul http Node, jadi ketika suatu hari Anda ingin menjalankan handler yang sama di runtime edge, Anda harus menulis ulang. Hono mengambil taruhan sebaliknya. Ia dibangun di atas objek Request dan Response Web Standard — primitif yang sama yang sudah Anda pakai di browser dan di Service Worker — sehingga framework itu sendiri tidak membawa asumsi runtime apa pun. Per Juli 2026 versinya v4.12, tanpa dependensi, dan preset hono/tiny beratnya di bawah 12kB.
Manfaat praktisnya adalah portabilitas. Saya bisa membuat prototipe API di Bun secara lokal untuk startup cepat, men-deploy-nya ke Cloudflare Workers untuk baca latensi rendah global, dan tetap menyimpan build Node untuk VPS lama — semuanya dari pohon sumber yang sama. Artikel ini membahas routing, middleware, adapter runtime, dan klien RPC bertipe, dengan trade-off yang benar-benar saya temui.
RegExpRouter bawaan Hono mengompilasi setiap rute yang Anda daftarkan menjadi satu ekspresi reguler besar. Karena itu, mencocokkan path yang masuk mendekati waktu konstan berapa pun jumlah rutenya, bukan menelusuri radix tree per permintaan. Dalam benchmark yang dipublikasikan, ini membuat pencarian rute jauh lebih cepat daripada tree find-my-way yang menjalankan Fastify. API routing-nya sendiri akan terasa familier — handler metode, parameter path, dan wildcard — tetapi bentuk objek yang Anda sentuh murni Web Standard.
import { Hono } from 'hono'
const app = new Hono()
app.get('/health', (c) => c.json({ ok: true }))
app.get('/users/:id', (c) => {
const id = c.req.param('id') // typed path param
const verbose = c.req.query('verbose') // query string
return c.json({ id, verbose })
})
app.post('/users', async (c) => {
const body = await c.req.json() // Web Standard Request under the hood
return c.json({ created: body }, 201)
})
export default appMiddleware di Hono adalah fungsi yang menerima konteks dan callback next. Anda melakukan pekerjaan sebelum memanggil next, menunggunya, lalu melakukan pekerjaan sesudahnya — model onion klasik, tetapi setiap bagian portabel karena tak satu pun menjangkau global Node. Paket resminya mencakup daftar yang membosankan-tapi-esensial: CORS, verifikasi JWT, bearer auth, logging permintaan, kompresi, ETag, dan rate limiting. Anda memasangnya per path atau secara global.
import { Hono } from 'hono'
import { logger } from 'hono/logger'
import { cors } from 'hono/cors'
import { jwt } from 'hono/jwt'
const app = new Hono()
app.use('*', logger())
app.use('/api/*', cors({ origin: 'https://www.matthewswong.com' }))
app.use('/api/*', jwt({ secret: process.env.JWT_SECRET! }))
// custom middleware — same signature
app.use('*', async (c, next) => {
const start = Date.now()
await next()
c.header('X-Response-Time', `${Date.now() - start}ms`)
})Urutan penting dan short-circuit bersifat eksplisit: jika sebuah middleware mengembalikan Response alih-alih memanggil next, rantainya berhenti di situ. Begitulah guard auth menolak permintaan dengan rapi — kembalikan 401 dan jangan pernah menyentuh handler.
Bagian inilah yang meyakinkan saya. Aplikasi Hono Anda hanyalah objek dengan metode fetch yang menerima Request dan mengembalikan Response. Setiap runtime punya cara sendiri memasukkan permintaan, jadi Hono menyediakan adapter tipis per platform — kode aplikasi tidak pernah berubah, hanya file entry-nya. Cloudflare Workers dan Bun sama-sama berbicara fetch secara native, jadi Anda mengekspor aplikasi secara langsung. Node butuh jembatan dari modul http-nya, yang disediakan oleh adapter Node.
// app.ts — shared by ALL runtimes, never changes
import { Hono } from 'hono'
export const app = new Hono().get('/', (c) => c.text('Hello from anywhere'))
// --- Cloudflare Workers: worker.ts
import { app } from './app'
export default app // Workers calls app.fetch
// --- Bun: bun.ts -> bun run bun.ts
import { app } from './app'
export default { fetch: app.fetch, port: 3000 }
// --- Node: node.ts -> node node.ts
import { serve } from '@hono/node-server'
import { app } from './app'
serve({ fetch: app.fetch, port: 3000 })Portabilitas berakhir di titik API platform dimulai. Jika sebuah handler menjangkau binding KV Cloudflare, API file khusus Bun, atau stream Node, rute itu berhenti menjadi portabel. Simpan akses spesifik-runtime di balik antarmuka kecil yang bisa Anda tukar, atau Anda akan kehilangan properti yang justru menjadi alasan Anda memilih Hono.
Fitur RPC Hono membagikan tipe rute Anda langsung ke frontend. Tidak ada file OpenAPI dan tidak ada langkah pembuatan kode — Anda mengekspor satu tipe TypeScript dari server dan klien hc membacanya. Jebakannya, dan ini footgun paling umum, adalah inferensi hanya bertahan jika Anda merangkai handler Anda menjadi satu ekspresi. Pecah rantai menjadi pernyataan terpisah dan klien kehilangan tipenya. Saya memakai validator permintaan agar input dan output yang diinferensikan benar-benar ditegakkan saat runtime, bukan hanya di editor.
// server.ts
import { Hono } from 'hono'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'
const routes = new Hono()
.post(
'/posts',
zValidator('json', z.object({ title: z.string() })),
(c) => {
const { title } = c.req.valid('json')
return c.json({ id: 1, title }, 201)
},
) // <-- keep the chain unbroken so types are inferred
export type AppType = typeof routes
export default routes
// client.ts — frontend, fully typed, no codegen
import { hc } from 'hono/client'
import type { AppType } from './server'
const client = hc<AppType>('https://api.example.com')
const res = await client.posts.$post({ json: { title: 'Hello' } })
const data = await res.json() // data.title is typed as stringUntuk API besar, kompilasi tipe klien lebih awal alih-alih membiarkan editor menghitungnya ulang di setiap ketukan tombol. Dokumentasinya menyarankan membungkus hc dalam helper yang tipe kembaliannya dipatok dengan ReturnType, sehingga tsc melakukan instansiasi tipe berat sekali saat build dan IDE Anda tetap cepat.
Saya memilih Hono ketika beban kerjanya berbentuk API dan saya peduli untuk men-deploy ke lebih dari satu tempat: fungsi edge, layanan Bun, kontainer Node kecil. Ia sengaja tidak beropini tentang lapisan di atas HTTP, jadi jika Anda ingin dependency injection, decorator, dan sistem modul yang kaku, framework serba-lengkap seperti NestJS akan terasa lebih utuh. Tabel di bawah adalah cara saya memutuskan.
| Pertimbangan | Hono | Express |
|---|---|---|
| Dibangun di atas | Web Standards (fetch) | Modul http Node |
| Runtime | Workers, Bun, Node, Deno | Hanya Node |
| Dependensi | Nol | Beberapa transitif |
| Klien bertipe | RPC bawaan, tanpa codegen | Tidak ada |
| Router | RegExpRouter prakompilasi | Pencocokan middleware linier |
Tak satu pun dari ini membuat Express salah — ia membuat Hono default yang lebih baik untuk era edge. Jika API Anda mungkin hidup lebih lama dari runtime pertamanya, menulisnya terhadap Web Standards hari ini berarti Anda tidak menulis ulang di hari Anda pindah. Properti tunggal itu telah menghemat waktu saya lebih banyak daripada micro-benchmark mana pun.