Hono is a fast, lightweight web framework that runs anywhere JavaScript is supported, built using web standards. Of course, it runs on Cloudflare Workers.
Three years ago, in December 2021, I wanted to create applications for Cloudflare Workers, but without a framework, the code became too verbose, and I couldn’t find a framework that met my needs. Itty-router was very nice but too simple. Worktop and Sunder did what I wanted, but their APIs weren’t quite to my liking. I was also interested in creating a router based on a Trie structure because it offers high speed. So, I started building a web framework with a Trie-based router.
“When I was trying to create my applications, I ended up creating a framework for them” — a classic example of yak shaving. However, now Hono is used by many developers, including Cloudflare, which uses Hono in its core products. So, this journey into yak shaving was ultimately meaningful.
Write once, run anywhere
Hono truly runs everywhere, not just on Cloudflare Workers. I’ll explain why later in the article, but Hono also works on Deno, Bun, and Node.js. This is because Hono does not rely on external libraries and instead uses only the web standards APIs supported by each of these environments.
For developers, it is a joy to know that the same code can run across different environments. For example, the following src/index.ts code will run on Cloudflare Workers, Deno, and Bun:
import { Hono } from 'hono'
const app = new Hono()
app.get('/hello', (c) => c.text('Hello Hono!'))
export default app
To run it on Cloudflare Workers, execute the Wrangler command:
wrangler dev src/index.ts
The same code works on Deno:
deno serve src/index.ts
And on Bun:
bun run src/index.ts
This is just a simple “Hello World” example, but more complex applications with middleware and helper functions, which I’ll discuss below, can also run on Cloudflare Workers or other environments. To prove this, almost all our test code for Hono itself runs identically on these environments. This is a genuine “write once, run anywhere” experience.
Who is using Hono?
Hono is now used by many developers and companies. For example, Unkey deploys their application built with Hono’s OpenAPI feature on Cloudflare Workers. Below is a list of companies using Hono, based on my survey “Who is using Hono in production?”:
- Cloudflare
- Nodecraft
- OpenStatus
- Unkey
- Goens
- NOT A HOTEL
- CyberAgent
- AI shift
- Hanabi.rest
- BaseAI
There are many more companies not listed here. Major web services and libraries, such as Prisma, Resend, Vercel AI SDK, Supabase, and Upstash, use Hono in their examples. There are also several influencers who prefer Hono as an alternative to Express.
Of course, at Cloudflare, we also use Hono. D1 uses Hono for the internal web API running on Workers. Workers Logs is based on code from Baselime (acquired by Cloudflare) and uses Hono to migrate applications from their original infrastructure to Cloudflare Workers. All Workers Logs internal or customer-facing APIs run on Workers using Hono. We also use Hono in the internals of many other products, such as KV and Queues.
Why create a multi-runtime framework?
You might wonder: “Why is a Cloudflare employee creating a framework that runs everywhere?” Initially, Hono was designed to work exclusively with Cloudflare Workers. However, starting from version 2, I added support for Deno and Bun. This was a very wise decision. If Hono had been targeted only at Cloudflare Workers, it might not have attracted as many users. By running on more runtimes, it gains more users, leading to the discovery of bugs and more feedback, which ultimately leads to higher-quality software.
Hono and Cloudflare — a perfect combo
The combination of Hono and Cloudflare offers a delightful developer experience.
Many websites, including our Cloudflare Docs, introduce the following “vanilla” JavaScript as a “Hello World” for Cloudflare Workers:
export default {
fetch: () => {
return new Response('Hello World!')
}
}
This is primitive and good for understanding the Workers principle. However, if you want to create an endpoint that “returns a JSON response for GET requests that come to /books,” you need to write something like this:
export default {
fetch: (req) => {
const url = new URL(req.url)
if (req.method === 'GET' && url.pathname === '/books') {
return Response.json({
ok: true
})
}
return Response.json(
{
ok: false
},
{
status: 404
}
)
}
}
If you use Hono, the code looks like this:
import { Hono } from 'hono'
const app = new Hono()
app.get('/books', (c) => {
return c.json({
ok: true
})
})
export default app
It’s short, and you can intuitively understand that it handles GET requests to /books.
If you want to handle GET requests to /authors/yusuke and extract “yusuke” from the path, where “yusuke” is a variable, you would need to add something more complex in vanilla JavaScript:
if (req.method === 'GET') {
const match = url.pathname.match(/^\/authors\/([^\/]+)/)
if (match) {
const author = match[1]
return Response.json({
Author: author
})
}
}
If you use Hono, you don’t need if statements. Just add the endpoint definition to the app. Also, you don’t need to write a regular expression to get “yusuke”. You can get it using the function c.req.param():
app.get('/authors/:name', (c) => {
const author = c.req.param('name')
return c.json({
Author: author
})
})
One or two routes may be fine, but any more than that, and maintenance becomes tricky. Code becomes more complex, and bugs are harder to find. Using Hono, the code is very neat.
It is also easy to work with bindings to Cloudflare products such as KV, R2, D1, etc., because Hono uses a “context model.” A context is a container that holds the application’s state until a request is received, and a response is returned. You can use context to retrieve a request object, set response headers, and create custom variables. It also holds Cloudflare bindings. For example, if you set up a Cloudflare KV namespace with the name MY_KV, you can access it as follows, with TypeScript type completion:
import { Hono } from 'hono'
type Env = {
Bindings: {
MY_KV: KVNamespace
}
}
const app = new Hono<Env>()
app.post('/message', async (c) => {
const message = c.req.query('message') ?? 'Hi'
await c.env.MY_KV.put('message', message)
return c.text(`message is set`, 201)
})
Hono allows you to write code in a simple and intuitive way, but that doesn’t mean there are limitations. With Hono, you can do everything possible on Cloudflare Workers.